Index: head/sys/conf/files =================================================================== --- head/sys/conf/files (revision 62586) +++ head/sys/conf/files (revision 62587) @@ -1,924 +1,936 @@ # $FreeBSD$ # # The long compile-with and dependency lines are required because of # limitations in config: backslash-newline doesn't work in strings, and # dependency lines other than the first are silently ignored. # aicasm optional ahc \ dependency "$S/dev/aic7xxx/*.[chyl]" \ compile-with "${MAKE} -f $S/dev/aic7xxx/Makefile MAKESRCPATH=$S/dev/aic7xxx" \ no-obj no-implicit-rule \ clean "aicasm aicasm_gram.c aicasm_scan.c y.tab.h" aic7xxx_{seq,reg}.h optional ahc \ compile-with "./aicasm ${INCLUDES} -o aic7xxx_seq.h -r aic7xxx_reg.h $S/dev/aic7xxx/aic7xxx.seq" \ no-obj no-implicit-rule before-depend \ clean "aic7xxx_seq.h aic7xxx_reg.h" \ dependency "$S/dev/aic7xxx/aic7xxx.{reg,seq} $S/cam/scsi/scsi_message.h aicasm" kern/device_if.m standard kern/bus_if.m standard kern/linker_if.m standard cam/cam.c optional scbus cam/cam_extend.c optional scbus cam/cam_periph.c optional scbus cam/cam_queue.c optional scbus cam/cam_sim.c optional scbus cam/cam_xpt.c optional scbus cam/scsi/scsi_all.c optional scbus cam/scsi/scsi_cd.c optional cd cam/scsi/scsi_ch.c optional ch cam/scsi/scsi_da.c optional da cam/scsi/scsi_pass.c optional pass cam/scsi/scsi_pt.c optional pt cam/scsi/scsi_sa.c optional sa cam/scsi/scsi_scan.c optional scan cam/scsi/scsi_ses.c optional ses cam/scsi/scsi_targ_bh.c optional targbh cam/scsi/scsi_target.c optional targ coda/coda_fbsd.c count vcoda coda/coda_namecache.c optional vcoda coda/coda_psdev.c optional vcoda coda/coda_subr.c optional vcoda coda/coda_venus.c optional vcoda coda/coda_vfsops.c optional vcoda coda/coda_vnops.c optional vcoda crypto/blowfish/bf_cbc.c optional ipsec ipsec_esp crypto/blowfish/bf_cbc_m.c optional ipsec ipsec_esp crypto/blowfish/bf_enc.c optional ipsec ipsec_esp crypto/blowfish/bf_skey.c optional ipsec ipsec_esp crypto/cast128/cast128.c optional ipsec ipsec_esp crypto/cast128/cast128_cbc.c optional ipsec ipsec_esp crypto/des/des_3cbc.c optional ipsec ipsec_esp crypto/des/des_cbc.c optional ipsec ipsec_esp crypto/des/des_ecb.c optional ipsec ipsec_esp crypto/des/des_setkey.c optional ipsec ipsec_esp crypto/rc5/rc5.c optional ipsec ipsec_esp crypto/rc5/rc5_cbc.c optional ipsec ipsec_esp crypto/sha1.c optional ipsec ddb/db_access.c optional ddb ddb/db_break.c optional ddb ddb/db_command.c optional ddb ddb/db_examine.c optional ddb ddb/db_expr.c optional ddb ddb/db_input.c optional ddb ddb/db_kld.c optional ddb ddb/db_lex.c optional ddb ddb/db_output.c optional ddb ddb/db_print.c optional ddb ddb/db_ps.c optional ddb ddb/db_run.c optional ddb ddb/db_sym.c optional ddb ddb/db_sysctl.c optional ddb ddb/db_trap.c optional ddb ddb/db_variables.c optional ddb ddb/db_watch.c optional ddb ddb/db_write_cmd.c optional ddb dev/advansys/adv_eisa.c optional adv eisa dev/advansys/adv_pci.c optional adv pci dev/advansys/advansys.c optional adv dev/advansys/advlib.c optional adv dev/advansys/advmcode.c optional adv dev/advansys/adw_pci.c optional adw pci dev/advansys/adwcam.c optional adw dev/advansys/adwlib.c optional adw dev/advansys/adwmcode.c optional adw dev/aha/aha.c count aha dev/aha/aha_isa.c optional aha isa dev/aha/aha_mca.c optional aha mca dev/ahb/ahb.c optional ahb eisa dev/aic/aic.c optional aic dev/aic/aic_pccard.c optional aic card dev/aic7xxx/93cx6.c optional ahc dev/aic7xxx/ahc_eisa.c optional ahc eisa \ dependency "aic7xxx_reg.h $S/dev/aic7xxx/ahc_eisa.c" dev/aic7xxx/ahc_pci.c optional ahc pci \ dependency "aic7xxx_reg.h $S/dev/aic7xxx/ahc_pci.c" dev/aic7xxx/aic7xxx.c optional ahc \ dependency "aic7xxx_{reg,seq}.h" dev/amr/amr.c optional amr dev/amr/amr_disk.c optional amr dev/amr/amr_pci.c optional amr dev/an/if_an.c optional an dev/an/if_an_isa.c optional an isa dev/an/if_an_pccard.c optional an card dev/an/if_an_pci.c optional an pci dev/awi/am79c930.c optional awi dev/awi/awi.c optional awi dev/awi/if_awi_pccard.c optional awi card dev/bktr/bktr_audio.c optional bktr pci dev/bktr/bktr_card.c optional bktr pci dev/bktr/bktr_core.c count bktr pci dev/bktr/bktr_i2c.c optional bktr pci smbus dev/bktr/bktr_os.c optional bktr pci dev/bktr/bktr_tuner.c optional bktr pci dev/buslogic/bt.c optional bt dev/buslogic/bt_eisa.c optional bt eisa dev/buslogic/bt_isa.c optional bt isa dev/buslogic/bt_mca.c optional bt mca dev/buslogic/bt_pci.c optional bt pci dev/cardbus/cardbus.c optional cardbus dev/ccd/ccd.c count ccd dev/cs/if_cs.c optional cs #dev/dpt/dpt_control.c optional dpt dev/dpt/dpt_eisa.c optional dpt eisa dev/dpt/dpt_pci.c optional dpt pci dev/dpt/dpt_scsi.c optional dpt dev/ed/if_ed_pci.c optional ed pci dev/en/midway.c count en dev/ep/if_ep.c optional ep dev/ep/if_ep_eisa.c optional ep eisa dev/ep/if_ep_isa.c optional ep isa dev/ep/if_ep_mca.c optional ep mca dev/ep/if_ep_pccard.c optional ep card dev/ex/if_ex.c optional ex dev/ex/if_ex_isa.c optional ex isa dev/hea/eni.c optional hea dev/hea/eni_buffer.c optional hea dev/hea/eni_globals.c optional hea dev/hea/eni_if.c optional hea dev/hea/eni_init.c optional hea dev/hea/eni_intr.c optional hea dev/hea/eni_receive.c optional hea dev/hea/eni_transmit.c optional hea dev/hea/eni_vcm.c optional hea dev/hfa/fore_buffer.c optional hfa dev/hfa/fore_command.c optional hfa dev/hfa/fore_globals.c optional hfa dev/hfa/fore_if.c optional hfa dev/hfa/fore_init.c optional hfa dev/hfa/fore_intr.c optional hfa dev/hfa/fore_load.c optional hfa dev/hfa/fore_output.c optional hfa dev/hfa/fore_receive.c optional hfa dev/hfa/fore_stats.c optional hfa dev/hfa/fore_timer.c optional hfa dev/hfa/fore_transmit.c optional hfa dev/hfa/fore_vcm.c optional hfa dev/ida/ida.c optional ida dev/ida/ida_disk.c optional ida dev/ida/ida_eisa.c optional ida eisa dev/ida/ida_pci.c optional ida pci dev/ie/if_ie.c count ie isa dev/iicbus/iicbb_if.m optional iicbb dev/iicbus/iicbus_if.m optional iicbus dev/iicbus/if_ic.c optional ic dev/iicbus/iic.c optional iic dev/iicbus/iicbb.c optional iicbb dev/iicbus/iicbus.c optional iicbus dev/iicbus/iiconf.c optional iicbus dev/iicbus/iicsmb.c optional iicsmb \ dependency "iicbus_if.h" dev/isp/isp.c optional isp dev/isp/isp_freebsd.c optional isp dev/isp/isp_target.c optional isp dev/ispfw/ispfw.c optional ispfw dev/lmc/if_lmc.c optional lmc dev/lnc/if_lnc.c count lnc dev/lnc/if_lnc_isa.c optional lnc isa dev/lnc/if_lnc_pc98.c optional lnc isa dev/lnc/if_lnc_pci.c optional lnc pci dev/mca/mca_bus.c optional mca dev/md/md.c optional md dev/mii/amphy.c optional miibus dev/mii/brgphy.c optional miibus dev/mii/dcphy.c optional miibus dev/mii/exphy.c optional miibus dev/mii/mii.c optional miibus dev/mii/mii_physubr.c optional miibus dev/mii/mlphy.c optional miibus dev/mii/nsphy.c optional miibus dev/mii/pnphy.c optional miibus dev/mii/rlphy.c optional miibus dev/mii/tlphy.c optional miibus dev/mii/ukphy.c optional miibus dev/mii/ukphy_subr.c optional miibus dev/mii/xmphy.c optional miibus dev/mii/miibus_if.m optional miibus dev/mlx/mlx.c optional mlx dev/mlx/mlx_disk.c optional mlx dev/mlx/mlx_pci.c optional mlx dev/nulldev/nulldev.c standard dev/pccard/card_if.m optional card dev/pccard/card_if.m optional pccard dev/pccard/pccard.c optional pccard dev/pccard/pccard_cis.c optional pccard dev/pccard/pccard_cis_quirks.c optional pccard dev/pccard/power_if.m optional pccard dev/pcic/i82365.c optional pcic pccard dev/pcic/i82365_isa.c optional pcic pccard dev/pdq/if_fea.c optional fea eisa dev/pdq/if_fpa.c count fpa pci dev/pdq/pdq.c optional fea eisa dev/pdq/pdq.c optional fpa pci dev/pdq/pdq_ifsubr.c optional fea eisa dev/pdq/pdq_ifsubr.c optional fpa pci dev/ppbus/ppbus_if.m optional ppbus dev/ppbus/if_plip.c optional plip dev/ppbus/immio.c optional vpo dev/ppbus/lpbb.c optional lpbb dev/ppbus/lpt.c optional lpt dev/ppbus/pcfclock.c optional pcfclock dev/ppbus/ppb_1284.c optional ppbus dev/ppbus/ppb_base.c optional ppbus dev/ppbus/ppb_msq.c optional ppbus dev/ppbus/ppbconf.c optional ppbus dev/ppbus/ppi.c optional ppi dev/ppbus/pps.c optional pps dev/ppbus/vpo.c optional vpo dev/ppbus/vpoio.c optional vpo dev/randomdev/randomdev.c optional randomdev dev/randomdev/yarrow.c optional randomdev crypto/blowfish/bf_cbc.c optional randomdev crypto/blowfish/bf_enc.c optional randomdev crypto/blowfish/bf_skey.c optional randomdev dev/rp/rp.c optional rp dev/rp/rp_isa.c optional rp isa dev/rp/rp_pci.c optional rp pci dev/si/si.c optional si dev/si/si2_z280.c optional si dev/si/si3_t225.c optional si dev/si/si_eisa.c optional si eisa dev/si/si_isa.c optional si isa dev/si/si_pci.c optional si pci dev/smbus/smbus_if.m optional smbus dev/smbus/smb.c optional smb dev/smbus/smbconf.c optional smbus dev/smbus/smbus.c count smbus dev/sn/if_sn.c optional sn dev/sn/if_sn_isa.c optional sn isa dev/sn/if_sn_pccard.c optional sn card dev/sound/isa/ad1816.c optional pcm isa dev/sound/isa/es1888.c optional pcm isa dev/sound/isa/ess.c optional pcm isa dev/sound/isa/gusc.c optional gusc isa dev/sound/isa/gusc.c optional pcm isa dev/sound/isa/mss.c optional pcm isa dev/sound/isa/sb.c optional pcm isa dev/sound/isa/sbc.c optional pcm isa dev/sound/isa/sbc.c optional sbc isa #dev/sound/pci/aureal.c optional pcm pci dev/sound/pci/csa.c optional csa pci dev/sound/pci/csa.c optional pcm pci dev/sound/pci/csapcm.c optional pcm pci dev/sound/pci/ds1.c optional pcm pci dev/sound/pci/emu10k1.c optional pcm pci dev/sound/pci/es137x.c optional pcm pci dev/sound/pci/neomagic.c optional pcm pci dev/sound/pci/t4dwave.c optional pcm pci #dev/sound/pci/via82c686.c optional pcm pci dev/sound/pcm/ac97.c optional pcm dev/sound/pcm/channel.c optional pcm dev/sound/pcm/dsp.c optional pcm dev/sound/pcm/fake.c optional pcm dev/sound/pcm/feeder.c optional pcm dev/sound/pcm/mixer.c optional pcm dev/sound/pcm/sound.c optional pcm dev/streams/streams.c optional streams dev/sym/sym_hipd.c optional sym \ dependency "$S/dev/sym/sym_{conf,defs}.h" dev/tdfx/tdfx_pci.c optional tdfx # # USB support dev/usb/usb_if.m optional usb dev/usb/hid.c optional usb dev/usb/if_aue.c optional aue dev/usb/if_cue.c optional cue dev/usb/if_kue.c optional kue dev/usb/ohci.c optional ohci dev/usb/udbp.c optional udbp dev/usb/ugen.c optional ugen dev/usb/uhci.c optional uhci dev/usb/uhid.c optional uhid dev/usb/uhub.c optional usb dev/usb/ukbd.c optional ukbd dev/usb/ulpt.c optional ulpt dev/usb/umass.c optional umass dev/usb/ums.c optional ums dev/usb/urio.c optional urio dev/usb/usb.c optional usb dev/usb/usb_ethersubr.c optional usb #dev/usb/usb_mem.c optional usb dev/usb/usb_quirks.c optional usb dev/usb/usb_subr.c optional usb dev/usb/usbdi.c optional usb dev/usb/usbdi_util.c optional usb dev/vinum/vinum.c optional vinum dev/vinum/vinumconfig.c optional vinum dev/vinum/vinumdaemon.c optional vinum dev/vinum/vinuminterrupt.c optional vinum dev/vinum/vinumio.c optional vinum dev/vinum/vinumioctl.c optional vinum dev/vinum/vinumlock.c optional vinum dev/vinum/vinummemory.c optional vinum dev/vinum/vinumparser.c optional vinum dev/vinum/vinumraid5.c optional vinum dev/vinum/vinumrequest.c optional vinum dev/vinum/vinumrevive.c optional vinum dev/vinum/vinumstate.c optional vinum dev/vinum/vinumutil.c optional vinum dev/vn/vn.c optional vn dev/vx/if_vx.c count vx dev/vx/if_vx_eisa.c optional vx eisa dev/vx/if_vx_pci.c optional vx pci dev/xe/if_xe.c optional xe gnu/ext2fs/ext2_alloc.c optional ext2fs gnu/ext2fs/ext2_balloc.c optional ext2fs gnu/ext2fs/ext2_inode.c optional ext2fs gnu/ext2fs/ext2_inode_cnv.c optional ext2fs gnu/ext2fs/ext2_linux_balloc.c optional ext2fs gnu/ext2fs/ext2_linux_ialloc.c optional ext2fs gnu/ext2fs/ext2_lookup.c optional ext2fs gnu/ext2fs/ext2_subr.c optional ext2fs gnu/ext2fs/ext2_vfsops.c optional ext2fs gnu/ext2fs/ext2_vnops.c optional ext2fs # device drivers i4b/driver/i4b_trace.c count i4btrc i4b/driver/i4b_rbch.c count i4brbch i4b/driver/i4b_tel.c count i4btel i4b/driver/i4b_ipr.c count i4bipr i4b/driver/i4b_ctl.c count i4bctl i4b/driver/i4b_isppp.c count i4bisppp net/if_spppsubr.c count sppp # needed by i4bipr net/slcompress.c optional i4bipr # tina-dd control driver i4b/tina-dd/i4b_tina_dd.c count tina # support i4b/layer2/i4b_mbuf.c optional i4btrc # Q.921 handler i4b/layer2/i4b_l2.c count i4bq921 i4b/layer2/i4b_l2fsm.c optional i4bq921 i4b/layer2/i4b_uframe.c optional i4bq921 i4b/layer2/i4b_tei.c optional i4bq921 i4b/layer2/i4b_sframe.c optional i4bq921 i4b/layer2/i4b_iframe.c optional i4bq921 i4b/layer2/i4b_l2timer.c optional i4bq921 i4b/layer2/i4b_util.c optional i4bq921 i4b/layer2/i4b_lme.c optional i4bq921 # Q.931 handler i4b/layer3/i4b_q931.c count i4bq931 i4b/layer3/i4b_l3fsm.c optional i4bq931 i4b/layer3/i4b_l3timer.c optional i4bq931 i4b/layer3/i4b_l2if.c optional i4bq931 i4b/layer3/i4b_l4if.c optional i4bq931 i4b/layer3/i4b_q932fac.c optional i4bq931 # isdn device driver, interface to i4bd i4b/layer4/i4b_i4bdrv.c optional i4b i4b/layer4/i4b_l4.c count i4b i4b/layer4/i4b_l4mgmt.c optional i4b i4b/layer4/i4b_l4timer.c optional i4b isa/isa_if.m optional isa isa/isa_common.c count isa isa/isahint.c optional isa isa/joy.c optional joy isa/pnp.c optional isa isa/pnpparse.c optional isa isofs/cd9660/cd9660_bmap.c optional cd9660 isofs/cd9660/cd9660_lookup.c optional cd9660 isofs/cd9660/cd9660_node.c optional cd9660 isofs/cd9660/cd9660_rrip.c optional cd9660 isofs/cd9660/cd9660_util.c optional cd9660 isofs/cd9660/cd9660_vfsops.c optional cd9660 isofs/cd9660/cd9660_vnops.c optional cd9660 kern/imgact_aout.c standard kern/imgact_elf.c standard kern/imgact_gzip.c optional gzip kern/imgact_shell.c standard kern/inflate.c optional gzip kern/init_main.c standard kern/init_sysent.c standard kern/kern_acct.c standard kern/kern_accf.c standard kern/kern_acl.c standard kern/kern_cap.c standard kern/kern_clock.c standard kern/kern_conf.c standard kern/kern_descrip.c standard kern/kern_environment.c standard kern/kern_event.c standard kern/kern_exec.c standard kern/kern_exit.c standard kern/kern_fork.c standard kern/kern_intr.c standard kern/kern_jail.c standard kern/kern_kthread.c standard kern/kern_ktrace.c standard kern/kern_linker.c standard kern/kern_lock.c standard kern/kern_lockf.c standard kern/kern_malloc.c standard kern/kern_mib.c standard kern/kern_module.c standard kern/kern_ntptime.c standard kern/kern_physio.c standard kern/kern_proc.c standard kern/kern_prot.c standard kern/kern_resource.c standard kern/kern_shutdown.c standard kern/kern_sig.c standard kern/kern_subr.c standard kern/kern_switch.c standard kern/kern_synch.c standard kern/kern_syscalls.c standard kern/kern_sysctl.c standard kern/kern_tc.c standard kern/kern_threads.c standard kern/kern_time.c standard kern/kern_timeout.c standard kern/kern_xxx.c standard kern/link_aout.c standard kern/link_elf.c standard kern/md5c.c standard kern/subr_autoconf.c standard kern/subr_blist.c standard kern/subr_bus.c standard kern/subr_devstat.c standard kern/subr_disk.c standard kern/subr_diskslice.c standard kern/subr_eventhandler.c standard kern/subr_kobj.c standard kern/subr_log.c standard kern/subr_module.c standard kern/subr_prf.c standard kern/subr_prof.c standard kern/subr_rman.c standard kern/subr_scanf.c standard kern/subr_taskqueue.c standard kern/subr_xxx.c standard kern/sys_generic.c standard kern/sys_pipe.c standard kern/sys_process.c standard kern/sys_socket.c standard kern/sysv_ipc.c standard kern/sysv_msg.c optional sysvmsg kern/sysv_sem.c optional sysvsem kern/sysv_shm.c optional sysvshm kern/tty.c standard kern/tty_compat.c standard kern/tty_conf.c standard kern/tty_cons.c standard kern/tty_pty.c optional pty kern/tty_snoop.c count snp kern/tty_subr.c standard kern/tty_tty.c standard kern/uipc_domain.c standard kern/uipc_mbuf.c standard +kern/uipc_mbuf2.c standard kern/uipc_proto.c standard kern/uipc_socket.c standard kern/uipc_socket2.c standard kern/uipc_syscalls.c standard kern/uipc_usrreq.c standard kern/vfs_aio.c standard kern/vfs_bio.c standard kern/vfs_cache.c standard kern/vfs_cluster.c standard kern/vfs_conf.c standard kern/vfs_default.c standard kern/vfs_init.c standard kern/vfs_lookup.c standard kern/vfs_subr.c standard kern/vfs_syscalls.c standard kern/vfs_vnops.c standard # # These files in libkern/ are those needed by all architectures. Some # of the files in libkern/ are only needed on some architectures, e.g., # libkern/divdi3.c is needed by i386 but not alpha. Also, some of these # routines may be optimized for a particular platform. In either case, # the file should be moved to /conf/files. from here. # libkern/arc4random.c standard libkern/bcd.c standard libkern/index.c standard libkern/inet_ntoa.c standard libkern/mcount.c optional profiling-routine libkern/qsort.c standard libkern/random.c standard libkern/rindex.c standard libkern/scanc.c standard libkern/skpc.c standard libkern/strcat.c standard libkern/strcmp.c standard libkern/strcpy.c standard libkern/strlen.c standard libkern/strncmp.c standard libkern/strncpy.c standard libkern/strtol.c standard libkern/strtoq.c standard libkern/strtoul.c standard libkern/strtouq.c standard miscfs/deadfs/dead_vnops.c standard miscfs/devfs/devfs_tree.c optional devfs miscfs/devfs/devfs_vfsops.c optional devfs miscfs/devfs/devfs_vnops.c optional devfs miscfs/fdesc/fdesc_vfsops.c optional fdesc miscfs/fdesc/fdesc_vnops.c optional fdesc miscfs/fifofs/fifo_vnops.c standard miscfs/kernfs/kernfs_vfsops.c optional kernfs miscfs/kernfs/kernfs_vnops.c optional kernfs miscfs/nullfs/null_subr.c optional nullfs miscfs/nullfs/null_vfsops.c optional nullfs miscfs/nullfs/null_vnops.c optional nullfs miscfs/portal/portal_vfsops.c optional portal miscfs/portal/portal_vnops.c optional portal miscfs/procfs/procfs_ctl.c optional procfs miscfs/procfs/procfs_dbregs.c standard miscfs/procfs/procfs_fpregs.c standard miscfs/procfs/procfs_map.c optional procfs miscfs/procfs/procfs_mem.c standard miscfs/procfs/procfs_note.c optional procfs miscfs/procfs/procfs_regs.c standard miscfs/procfs/procfs_rlimit.c optional procfs miscfs/procfs/procfs_status.c optional procfs miscfs/procfs/procfs_subr.c optional procfs miscfs/procfs/procfs_type.c optional procfs miscfs/procfs/procfs_vfsops.c optional procfs miscfs/procfs/procfs_vnops.c optional procfs miscfs/specfs/spec_vnops.c standard miscfs/umapfs/umap_subr.c optional umapfs miscfs/umapfs/umap_vfsops.c optional umapfs miscfs/umapfs/umap_vnops.c optional umapfs miscfs/union/union_subr.c optional union miscfs/union/union_vfsops.c optional union miscfs/union/union_vnops.c optional union msdosfs/msdosfs_conv.c optional msdosfs msdosfs/msdosfs_denode.c optional msdosfs msdosfs/msdosfs_fat.c optional msdosfs msdosfs/msdosfs_lookup.c optional msdosfs msdosfs/msdosfs_vfsops.c optional msdosfs msdosfs/msdosfs_vnops.c optional msdosfs net/bpf.c standard net/bpf_filter.c count bpf net/bridge.c optional bridge net/bsd_comp.c optional ppp_bsdcomp #net/hostcache.c standard net/if.c standard net/if_atmsubr.c optional atm net/if_disc.c optional disc net/if_ef.c optional ef net/if_ethersubr.c optional ether net/if_faith.c count faith net/if_fddisubr.c optional fddi net/if_gif.c count gif net/if_iso88025subr.c optional token net/if_loop.c count loop net/if_media.c standard net/if_mib.c standard net/if_ppp.c count ppp net/if_sl.c optional sl net/if_spppsubr.c optional sppp +net/if_stf.c count stf net/if_tun.c optional tun net/if_vlan.c count vlan net/intrq.c standard net/net_osdep.c standard net/ppp_deflate.c optional ppp_deflate net/ppp_tty.c optional ppp net/pfil.c optional pfil_hooks ipfilter net/radix.c standard net/raw_cb.c standard net/raw_usrreq.c standard net/route.c standard net/rtsock.c standard net/slcompress.c optional ppp net/slcompress.c optional sl net/zlib.c optional ppp_deflate +net/zlib.c optional ipsec netatalk/aarp.c optional netatalk netatalk/at_control.c optional netatalk netatalk/at_proto.c optional netatalk netatalk/at_rmx.c optional netatalkdebug netatalk/ddp_input.c optional netatalk netatalk/ddp_output.c optional netatalk netatalk/ddp_usrreq.c optional netatalk netatm/atm_aal5.c optional atm_core netatm/atm_cm.c optional atm_core netatm/atm_device.c optional atm_core netatm/atm_if.c optional atm_core netatm/atm_proto.c optional atm_core netatm/atm_signal.c optional atm_core netatm/atm_socket.c optional atm_core netatm/atm_subr.c optional atm_core netatm/atm_usrreq.c optional atm_core netatm/ipatm/ipatm_event.c optional atm_ip atm_core netatm/ipatm/ipatm_if.c optional atm_ip atm_core netatm/ipatm/ipatm_input.c optional atm_ip atm_core netatm/ipatm/ipatm_load.c optional atm_ip atm_core netatm/ipatm/ipatm_output.c optional atm_ip atm_core netatm/ipatm/ipatm_usrreq.c optional atm_ip atm_core netatm/ipatm/ipatm_vcm.c optional atm_ip atm_core netatm/sigpvc/sigpvc_if.c optional atm_sigpvc atm_core netatm/sigpvc/sigpvc_subr.c optional atm_sigpvc atm_core netatm/spans/spans_arp.c optional atm_spans atm_core \ dependency "spans_xdr.h" netatm/spans/spans_cls.c optional atm_spans atm_core netatm/spans/spans_if.c optional atm_spans atm_core netatm/spans/spans_kxdr.c optional atm_spans atm_core netatm/spans/spans_msg.c optional atm_spans atm_core netatm/spans/spans_print.c optional atm_spans atm_core netatm/spans/spans_proto.c optional atm_spans atm_core netatm/spans/spans_subr.c optional atm_spans atm_core netatm/spans/spans_util.c optional atm_spans atm_core spans_xdr.h optional atm_spans atm_core \ before-depend \ dependency "$S/netatm/spans/spans_xdr.x" \ compile-with "rpcgen -h -C $S/netatm/spans/spans_xdr.x > spans_xdr.h" \ clean "spans_xdr.h" \ no-obj no-implicit-rule spans_xdr.c optional atm_spans atm_core \ before-depend \ dependency "$S/netatm/spans/spans_xdr.x" \ compile-with "rpcgen -c -C $S/netatm/spans/spans_xdr.x > spans_xdr.c" \ clean "spans_xdr.c" \ no-obj no-implicit-rule local spans_xdr.o optional atm_spans atm_core \ dependency "$S/netatm/spans/spans_xdr.x" \ compile-with "${NORMAL_C}" \ no-implicit-rule local netatm/uni/q2110_sigaa.c optional atm_uni atm_core netatm/uni/q2110_sigcpcs.c optional atm_uni atm_core netatm/uni/q2110_subr.c optional atm_uni atm_core netatm/uni/qsaal1_sigaa.c optional atm_uni atm_core netatm/uni/qsaal1_sigcpcs.c optional atm_uni atm_core netatm/uni/qsaal1_subr.c optional atm_uni atm_core netatm/uni/sscf_uni.c optional atm_uni atm_core netatm/uni/sscf_uni_lower.c optional atm_uni atm_core netatm/uni/sscf_uni_upper.c optional atm_uni atm_core netatm/uni/sscop.c optional atm_uni atm_core netatm/uni/sscop_lower.c optional atm_uni atm_core netatm/uni/sscop_pdu.c optional atm_uni atm_core netatm/uni/sscop_sigaa.c optional atm_uni atm_core netatm/uni/sscop_sigcpcs.c optional atm_uni atm_core netatm/uni/sscop_subr.c optional atm_uni atm_core netatm/uni/sscop_timer.c optional atm_uni atm_core netatm/uni/sscop_upper.c optional atm_uni atm_core netatm/uni/uni_load.c optional atm_uni atm_core netatm/uni/uniarp.c optional atm_uni atm_core netatm/uni/uniarp_cache.c optional atm_uni atm_core netatm/uni/uniarp_input.c optional atm_uni atm_core netatm/uni/uniarp_output.c optional atm_uni atm_core netatm/uni/uniarp_timer.c optional atm_uni atm_core netatm/uni/uniarp_vcm.c optional atm_uni atm_core netatm/uni/uniip.c optional atm_uni atm_core netatm/uni/unisig_decode.c optional atm_uni atm_core netatm/uni/unisig_encode.c optional atm_uni atm_core netatm/uni/unisig_if.c optional atm_uni atm_core netatm/uni/unisig_mbuf.c optional atm_uni atm_core netatm/uni/unisig_msg.c optional atm_uni atm_core netatm/uni/unisig_print.c optional atm_uni atm_core netatm/uni/unisig_proto.c optional atm_uni atm_core netatm/uni/unisig_sigmgr_state.c optional atm_uni atm_core netatm/uni/unisig_subr.c optional atm_uni atm_core netatm/uni/unisig_util.c optional atm_uni atm_core netatm/uni/unisig_vc_state.c optional atm_uni atm_core netgraph/ng_UI.c optional netgraph_UI netgraph/ng_async.c optional netgraph_async netgraph/ng_base.c optional netgraph netgraph/ng_bpf.c optional netgraph_bpf net/bpf_filter.c optional netgraph_bpf netgraph/ng_cisco.c optional netgraph_cisco netgraph/ng_echo.c optional netgraph_echo netgraph/ng_ether.c optional netgraph_ether netgraph/ng_frame_relay.c optional netgraph_frame_relay netgraph/ng_hole.c optional netgraph_hole netgraph/ng_iface.c optional netgraph_iface netgraph/ng_ksocket.c optional netgraph_ksocket netgraph/ng_lmi.c optional netgraph_lmi netgraph/ng_mppc.c optional netgraph_mppc_compression # The next two files (plus the header file net/mppc.h) are proprietary and # must be obtained elsewhere in order to enable NETGRAPH_MPPC_COMPRESSION net/mppcc.c optional netgraph_mppc_compression net/mppcd.c optional netgraph_mppc_compression netgraph/ng_mppc.c optional netgraph_mppc_encryption crypto/rc4/rc4.c optional netgraph_mppc_encryption crypto/sha1.c optional netgraph_mppc_encryption netgraph/ng_parse.c optional netgraph netgraph/ng_ppp.c optional netgraph_ppp netgraph/ng_pppoe.c optional netgraph_pppoe netgraph/ng_pptpgre.c optional netgraph_pptpgre netgraph/ng_rfc1490.c optional netgraph_rfc1490 netgraph/ng_socket.c optional netgraph_socket netgraph/ng_tee.c optional netgraph_tee netgraph/ng_tty.c optional netgraph_tty netgraph/ng_vjc.c optional netgraph_vjc net/slcompress.c optional netgraph_vjc netinet/accf_data.c optional accept_filter_data netinet/accf_http.c optional accept_filter_http netinet/fil.c optional ipfilter inet netinet/if_atm.c optional atm netinet/if_ether.c optional ether netinet/igmp.c optional inet netinet/in.c optional inet netinet/in_gif.c optional gif inet #netinet/in_hostcache.c optional inet netinet/in_pcb.c optional inet netinet/in_proto.c optional inet netinet/in_rmx.c optional inet netinet/ip_auth.c optional ipfilter inet netinet/ip_divert.c optional ipdivert netinet/ip_dummynet.c optional dummynet netinet/ip_ecn.c optional inet netinet/ip_ecn.c optional inet6 +netinet/ip_encap.c optional inet +netinet/ip_encap.c optional inet6 netinet/ip_fil.c optional ipfilter inet netinet/ip_flow.c optional inet netinet/ip_frag.c optional ipfilter inet netinet/ip_fw.c optional ipfirewall netinet/ip_icmp.c optional inet netinet/ip_input.c optional inet netinet/ip_log.c optional ipfilter inet netinet/ip_mroute.c optional inet netinet/ip_nat.c optional ipfilter inet netinet/ip_output.c optional inet netinet/ip_proxy.c optional ipfilter inet netinet/ip_state.c optional ipfilter inet netinet/mlfk_ipl.c optional ipfilter inet netinet/raw_ip.c optional inet netinet/tcp_debug.c optional tcpdebug netinet/tcp_input.c optional inet netinet/tcp_output.c optional inet netinet/tcp_subr.c optional inet netinet/tcp_timer.c optional inet netinet/tcp_usrreq.c optional inet netinet/udp_usrreq.c optional inet netinet6/ah_core.c optional ipsec netinet6/ah_input.c optional ipsec netinet6/ah_output.c optional ipsec netinet6/dest6.c optional inet6 netinet6/esp_core.c optional ipsec ipsec_esp netinet6/esp_input.c optional ipsec ipsec_esp netinet6/esp_output.c optional ipsec ipsec_esp netinet6/frag6.c optional inet6 netinet6/icmp6.c optional inet6 netinet6/in6.c optional inet6 netinet6/in6_cksum.c optional inet6 netinet6/in6_gif.c optional gif inet6 netinet6/in6_ifattach.c optional inet6 netinet6/in6_pcb.c optional inet6 netinet6/in6_prefix.c optional inet6 netinet6/in6_proto.c optional inet6 netinet6/in6_rmx.c optional inet6 +netinet6/in6_src.c optional inet6 netinet6/ip6_forward.c optional inet6 netinet6/ip6_fw.c optional inet6 ipv6firewall netinet6/ip6_input.c optional inet6 netinet6/ip6_mroute.c optional inet6 netinet6/ip6_output.c optional inet6 +netinet6/ipcomp_core.c optional ipsec +netinet6/ipcomp_input.c optional ipsec +netinet6/ipcomp_output.c optional ipsec netinet6/ipsec.c optional ipsec netinet6/mld6.c optional inet6 netinet6/nd6.c optional inet6 netinet6/nd6_nbr.c optional inet6 netinet6/nd6_rtr.c optional inet6 netinet6/raw_ip6.c optional inet6 netinet6/route6.c optional inet6 +netinet6/scope6.c optional inet6 +netinet6/udp6_output.c optional inet6 netinet6/udp6_usrreq.c optional inet6 netipx/ipx.c optional ipx netipx/ipx_cksum.c optional ipx netipx/ipx_input.c optional ipx netipx/ipx_ip.c optional ipx netipx/ipx_outputfl.c optional ipx netipx/ipx_pcb.c optional ipx netipx/ipx_proto.c optional ipx netipx/ipx_tun.c optional ipx netipx/ipx_usrreq.c optional ipx netipx/spx_debug.c optional ipx netipx/spx_usrreq.c optional ipx netkey/key.c optional ipsec +netkey/keydb.c optional ipsec netkey/key_debug.c optional ipsec netkey/keysock.c optional ipsec netnatm/natm.c optional natm netnatm/natm_pcb.c optional natm netnatm/natm_proto.c optional natm netncp/ncp_conn.c optional ncp netncp/ncp_crypt.c optional ncp netncp/ncp_login.c optional ncp netncp/ncp_mod.c optional ncp netncp/ncp_ncp.c optional ncp netncp/ncp_nls.c optional ncp netncp/ncp_rq.c optional ncp netncp/ncp_sock.c optional ncp netncp/ncp_subr.c optional ncp netns/idp_usrreq.c optional ns netns/ns.c optional ns netns/ns_error.c optional ns netns/ns_input.c optional ns netns/ns_ip.c optional ns netns/ns_output.c optional ns netns/ns_pcb.c optional ns netns/ns_proto.c optional ns netns/spp_debug.c optional ns netns/spp_usrreq.c optional ns nfs/bootp_subr.c optional bootp nfs/krpc_subr.c optional bootp nfs/nfs_bio.c optional nfs nfs/nfs_node.c optional nfs nfs/nfs_nqlease.c optional nfs nfs/nfs_serv.c optional nfs nfs/nfs_socket.c optional nfs nfs/nfs_srvcache.c optional nfs nfs/nfs_subs.c optional nfs nfs/nfs_syscalls.c optional nfs nfs/nfs_vfsops.c optional nfs nfs/nfs_vnops.c optional nfs ntfs/ntfs_compr.c optional ntfs ntfs/ntfs_ihash.c optional ntfs ntfs/ntfs_subr.c optional ntfs ntfs/ntfs_vfsops.c optional ntfs ntfs/ntfs_vnops.c optional ntfs nwfs/nwfs_io.c optional nwfs nwfs/nwfs_ioctl.c optional nwfs nwfs/nwfs_node.c optional nwfs nwfs/nwfs_subr.c optional nwfs nwfs/nwfs_vfsops.c optional nwfs nwfs/nwfs_vnops.c optional nwfs pccard/pccard.c count card pccard/pccard_beep.c optional card pccard/pccard_nbk.c optional card pccard/pcic.c optional pcic card pci/agp.c optional agp pci/agp_if.m optional agp pci/agp_intel.c optional agp pci/agp_via.c optional agp pci/agp_sis.c optional agp pci/agp_ali.c optional agp pci/agp_amd.c optional agp pci/alpm.c count alpm pci/amd.c optional amd pci/cy_pci.c optional cy pci pci/if_ar_p.c count ar pci pci/if_dc.c optional dc pci/if_de.c optional de pci/if_en_pci.c optional en pci pci/if_fxp.c optional fxp pci/if_mn.c optional mn pci/if_rl.c optional rl pci/if_sf.c optional sf pci/if_sis.c optional sis pci/if_sk.c optional sk pci/if_sr_p.c optional sr pci pci/if_ste.c optional ste pci/if_ti.c optional ti pci/if_tl.c optional tl pci/if_tx.c optional tx pci/if_vr.c optional vr pci/if_wb.c optional wb pci/if_wx.c optional wx pci/if_xl.c optional xl pci/intpm.c optional intpm pci/isp_pci.c optional isp pci/meteor.c count meteor pci pci/ncr.c optional ncr pci/ohci_pci.c optional ohci pci/pccbb.c optional pccbb cardbus pci/pci.c count pci pci/pci_compat.c optional pci compat_oldpci \ warning "Old PCI driver compatability shims present." pci/pcic_p.c optional pcic pci pci/pcisupport.c optional pci pci/pci_if.m optional pci pci/simos.c optional simos pci/uhci_pci.c optional uhci pci/xrpu.c optional xrpu posix4/ksched.c optional _kposix_priority_scheduling posix4/p1003_1b.c standard posix4/posix4_mib.c standard ufs/ffs/ffs_alloc.c optional ffs ufs/ffs/ffs_alloc.c optional mfs ufs/ffs/ffs_balloc.c optional ffs ufs/ffs/ffs_balloc.c optional mfs ufs/ffs/ffs_inode.c optional ffs ufs/ffs/ffs_inode.c optional mfs ufs/ffs/ffs_softdep.c optional softupdates ufs/ffs/ffs_softdep_stub.c standard ufs/ffs/ffs_subr.c optional ffs ufs/ffs/ffs_subr.c optional mfs ufs/ffs/ffs_tables.c optional ffs ufs/ffs/ffs_tables.c optional mfs ufs/ffs/ffs_vfsops.c optional ffs ufs/ffs/ffs_vfsops.c optional mfs ufs/ffs/ffs_vnops.c optional ffs ufs/ffs/ffs_vnops.c optional mfs ufs/mfs/mfs_vfsops.c optional mfs ufs/mfs/mfs_vnops.c optional mfs ufs/ufs/ufs_bmap.c standard ufs/ufs/ufs_disksubr.c standard ufs/ufs/ufs_extattr.c standard ufs/ufs/ufs_ihash.c standard ufs/ufs/ufs_inode.c standard ufs/ufs/ufs_lookup.c standard ufs/ufs/ufs_quota.c standard ufs/ufs/ufs_vfsops.c standard ufs/ufs/ufs_vnops.c standard vm/default_pager.c standard vm/device_pager.c standard vm/phys_pager.c standard vm/swap_pager.c standard vm/vm_fault.c standard vm/vm_glue.c standard vm/vm_init.c standard vm/vm_kern.c standard vm/vm_map.c standard vm/vm_meter.c standard vm/vm_mmap.c standard vm/vm_object.c standard vm/vm_page.c standard vm/vm_pageout.c standard vm/vm_pager.c standard vm/vm_swap.c standard vm/vm_unix.c standard vm/vm_zone.c standard vm/vnode_pager.c standard Index: head/sys/crypto/hmac_md5.c =================================================================== --- head/sys/crypto/hmac_md5.c (revision 62586) +++ head/sys/crypto/hmac_md5.c (nonexistent) @@ -1,98 +0,0 @@ -/* - * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the project nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * $FreeBSD$ - */ - -/* - * Based on sample code appeared on RFC2104. - */ - -#include -#include -#include -#include -#include - -#include - -void -hmac_md5(src0, srclen, key0, keylen, digest) - caddr_t src0; - size_t srclen; - caddr_t key0; - size_t keylen; - caddr_t digest; -{ - u_int8_t *src; - u_int8_t *key; - u_int8_t tk[16]; - u_int8_t ipad[65]; - u_int8_t opad[65]; - size_t i; - - src = (u_int8_t *)src0; - key = (u_int8_t *)key0; - - /* - * compress the key into 16bytes, if key is too long. - */ - if (64 < keylen) { - md5_init(); - md5_loop(key, keylen); - md5_pad(); - md5_result(&tk[0]); - key = &tk[0]; - keylen = 16; - } - - /* - * - */ - bzero(&ipad[0], sizeof ipad); - bzero(&opad[0], sizeof opad); - bcopy(key, &ipad[0], keylen); - bcopy(key, &opad[0], keylen); - - for (i = 0; i < 64; i++) { - ipad[i] ^= 0x36; - opad[i] ^= 0x5c; - } - - md5_init(); - md5_loop(&ipad[0], 64); - md5_loop(src, srclen); - md5_pad(); - md5_result((u_int8_t *)digest); - - md5_init(); - md5_loop(&opad[0], 64); - md5_loop((u_int8_t *)digest, 16); - md5_pad(); - md5_result((u_int8_t *)digest); -} Property changes on: head/sys/crypto/hmac_md5.c ___________________________________________________________________ Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Index: head/sys/crypto/hmac_md5.h =================================================================== --- head/sys/crypto/hmac_md5.h (revision 62586) +++ head/sys/crypto/hmac_md5.h (nonexistent) @@ -1,37 +0,0 @@ -/* - * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the project nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * $FreeBSD$ - */ - -#ifndef _NETINET6_HMAC_MD5_H_ -#define _NETINET6_HMAC_MD5_H_ - -extern void hmac_md5 __P((caddr_t, size_t, caddr_t, size_t, caddr_t)); - -#endif /* ! _NETINET6_HMAC_MD5_H_*/ Property changes on: head/sys/crypto/hmac_md5.h ___________________________________________________________________ Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Index: head/sys/crypto/blowfish/bf_cbc.c =================================================================== --- head/sys/crypto/blowfish/bf_cbc.c (revision 62586) +++ head/sys/crypto/blowfish/bf_cbc.c (revision 62587) @@ -1,150 +1,151 @@ +/* $FreeBSD$ */ +/* $KAME: bf_cbc.c,v 1.3 2000/03/27 04:36:25 sumikawa Exp $ */ + /* crypto/bf/bf_cbc.c */ /* Copyright (C) 1995-1997 Eric Young (eay@mincom.oz.au) * All rights reserved. * * This package is an SSL implementation written * by Eric Young (eay@mincom.oz.au). * The implementation was written so as to conform with Netscapes SSL. * * This library is free for commercial and non-commercial use as long as * the following conditions are aheared to. The following conditions * apply to all code found in this distribution, be it the RC4, RSA, * lhash, DES, etc., code; not just the SSL code. The SSL documentation * included with this distribution is covered by the same copyright terms * except that the holder is Tim Hudson (tjh@mincom.oz.au). * * Copyright remains Eric Young's, and as such any Copyright notices in * the code are not to be removed. * If this package is used in a product, Eric Young should be given attribution * as the author of the parts of the library used. * This can be in the form of a textual message at program startup or * in documentation (online or textual) provided with the package. * * 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 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 cryptographic software written by * Eric Young (eay@mincom.oz.au)" * The word 'cryptographic' can be left out if the rouines from the library * being used are not cryptographic related :-). * 4. If you include any Windows specific code (or a derivative thereof) from * the apps directory (application code) you must include an acknowledgement: * "This product includes software written by Tim Hudson (tjh@mincom.oz.au)" * * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * The licence and distribution terms for any publically available version or * derivative of this code cannot be changed. i.e. this code cannot simply be * copied and put under another distribution licence * [including the GNU Public Licence.] - * - * $FreeBSD$ */ #include #include void BF_cbc_encrypt(in, out, length, ks, iv, encrypt) unsigned char *in; unsigned char *out; long length; BF_KEY *ks; unsigned char *iv; int encrypt; { register BF_LONG tin0,tin1; register BF_LONG tout0,tout1,xor0,xor1; register long l=length; BF_LONG tin[2]; if (encrypt) { n2l(iv,tout0); n2l(iv,tout1); iv-=8; for (l-=8; l>=0; l-=8) { n2l(in,tin0); n2l(in,tin1); tin0^=tout0; tin1^=tout1; tin[0]=tin0; tin[1]=tin1; BF_encrypt(tin,ks,BF_ENCRYPT); tout0=tin[0]; tout1=tin[1]; l2n(tout0,out); l2n(tout1,out); } if (l != -8) { n2ln(in,tin0,tin1,l+8); tin0^=tout0; tin1^=tout1; tin[0]=tin0; tin[1]=tin1; BF_encrypt(tin,ks,BF_ENCRYPT); tout0=tin[0]; tout1=tin[1]; l2n(tout0,out); l2n(tout1,out); } l2n(tout0,iv); l2n(tout1,iv); } else { n2l(iv,xor0); n2l(iv,xor1); iv-=8; for (l-=8; l>=0; l-=8) { n2l(in,tin0); n2l(in,tin1); tin[0]=tin0; tin[1]=tin1; BF_encrypt(tin,ks,BF_DECRYPT); tout0=tin[0]^xor0; tout1=tin[1]^xor1; l2n(tout0,out); l2n(tout1,out); xor0=tin0; xor1=tin1; } if (l != -8) { n2l(in,tin0); n2l(in,tin1); tin[0]=tin0; tin[1]=tin1; BF_encrypt(tin,ks,BF_DECRYPT); tout0=tin[0]^xor0; tout1=tin[1]^xor1; l2nn(tout0,tout1,out,l+8); xor0=tin0; xor1=tin1; } l2n(xor0,iv); l2n(xor1,iv); } tin0=tin1=tout0=tout1=xor0=xor1=0; tin[0]=tin[1]=0; } Index: head/sys/crypto/blowfish/bf_cbc_m.c =================================================================== --- head/sys/crypto/blowfish/bf_cbc_m.c (revision 62586) +++ head/sys/crypto/blowfish/bf_cbc_m.c (revision 62587) @@ -1,340 +1,343 @@ +/* $FreeBSD$ */ +/* $KAME: bf_cbc_m.c,v 1.4 2000/06/14 10:41:16 itojun Exp $ */ + /* * heavily modified to accept mbuf, by Jun-ichiro itojun Itoh * , 1997. */ /* crypto/bf/bf_cbc.c */ /* Copyright (C) 1995-1997 Eric Young (eay@mincom.oz.au) * All rights reserved. * * This package is an SSL implementation written * by Eric Young (eay@mincom.oz.au). * The implementation was written so as to conform with Netscapes SSL. * * This library is free for commercial and non-commercial use as long as * the following conditions are aheared to. The following conditions * apply to all code found in this distribution, be it the RC4, RSA, * lhash, DES, etc., code; not just the SSL code. The SSL documentation * included with this distribution is covered by the same copyright terms * except that the holder is Tim Hudson (tjh@mincom.oz.au). * * Copyright remains Eric Young's, and as such any Copyright notices in * the code are not to be removed. * If this package is used in a product, Eric Young should be given attribution * as the author of the parts of the library used. * This can be in the form of a textual message at program startup or * in documentation (online or textual) provided with the package. * * 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 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 cryptographic software written by * Eric Young (eay@mincom.oz.au)" * The word 'cryptographic' can be left out if the rouines from the library * being used are not cryptographic related :-). * 4. If you include any Windows specific code (or a derivative thereof) from * the apps directory (application code) you must include an acknowledgement: * "This product includes software written by Tim Hudson (tjh@mincom.oz.au)" * * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * The licence and distribution terms for any publically available version or * derivative of this code cannot be changed. i.e. this code cannot simply be * copied and put under another distribution licence * [including the GNU Public Licence.] - * - * $FreeBSD$ */ #include #include #include #include #include -#define panic(x) {printf(x); return;} +#define panic(x) do { printf(x); return EINVAL; } while (0) -void BF_cbc_encrypt_m(m0, skip, length, key, iv, mode) +int BF_cbc_encrypt_m(m0, skip, length, key, iv, mode) struct mbuf *m0; int skip; int length; BF_KEY *key; unsigned char *iv; int mode; { u_int8_t inbuf[8], outbuf[8]; struct mbuf *m; size_t off; register BF_LONG tin0, tin1; register BF_LONG tout0, tout1; BF_LONG tin[2]; /* sanity checks */ if (m0->m_pkthdr.len < skip) { printf("mbuf length < skip\n"); - return; + return EINVAL; } if (m0->m_pkthdr.len < length) { printf("mbuf length < encrypt length\n"); - return; + return EINVAL; } if (m0->m_pkthdr.len < skip + length) { printf("mbuf length < skip + encrypt length\n"); - return; + return EINVAL; } if (length % 8) { printf("length is not multiple of 8\n"); - return; + return EINVAL; } m = m0; off = 0; /* skip over the header */ while (skip) { if (!m) panic("mbuf chain?\n"); if (m->m_len <= skip) { skip -= m->m_len; m = m->m_next; off = 0; } else { off = skip; skip = 0; } } /* initialize */ tin0 = tin1 = tout0 = tout1 = 0; tin[0] = tin[1] = 0; if (mode == BF_ENCRYPT) { u_int8_t *in, *out; n2l(iv, tout0); n2l(iv, tout1); while (0 < length) { if (!m) panic("mbuf chain?\n"); /* * copy the source into input buffer. * don't update off or m, since we need to use them * later. */ if (off + 8 <= m->m_len) bcopy(mtod(m, u_int8_t *) + off, &inbuf[0], 8); else { struct mbuf *n; size_t noff; u_int8_t *p; u_int8_t *in; n = m; noff = off; p = mtod(n, u_int8_t *) + noff; in = &inbuf[0]; while (in - &inbuf[0] < 8) { if (!p) panic("mbuf chain?\n"); - + *in++ = *p++; noff++; if (noff < n->m_len) continue; do { n = n->m_next; } while (n && ! n->m_len); noff = 0; if (n) p = mtod(n, u_int8_t *) + noff; else p = NULL; } } in = &inbuf[0]; out = &outbuf[0]; n2l(in, tin0); n2l(in, tin1); tin0 ^= tout0; tin[0] = tin0; tin1 ^= tout1; tin[1] = tin1; BF_encrypt(tin, key, BF_ENCRYPT); tout0 = tin[0]; l2n(tout0, out); tout1 = tin[1]; l2n(tout1, out); /* * copy the output buffer into the result. * need to update off and m. */ if (off + 8 < m->m_len) { bcopy(&outbuf[0], mtod(m, u_int8_t *) + off, 8); off += 8; } else if (off + 8 == m->m_len) { bcopy(&outbuf[0], mtod(m, u_int8_t *) + off, 8); do { m = m->m_next; } while (m && ! m->m_len); off = 0; } else { struct mbuf *n; size_t noff; u_int8_t *p; u_int8_t *out; n = m; noff = off; p = mtod(n, u_int8_t *) + noff; out = &outbuf[0]; while (out - &outbuf[0] < 8) { if (!p) panic("mbuf chain?"); *p++ = *out++; noff++; if (noff < n->m_len) continue; do { n = n->m_next; } while (n && ! n->m_len); noff = 0; if (n) p = mtod(n, u_int8_t *) + noff; else p = NULL; } m = n; off = noff; } length -= 8; } } else if (mode == BF_DECRYPT) { register BF_LONG xor0, xor1; u_int8_t *in, *out; xor0 = xor1 = 0; n2l(iv, xor0); n2l(iv, xor1); while (0 < length) { if (!m) panic("mbuf chain?\n"); /* * copy the source into input buffer. * don't update off or m, since we need to use them * later. */ if (off + 8 <= m->m_len) bcopy(mtod(m, u_int8_t *) + off, &inbuf[0], 8); else { struct mbuf *n; size_t noff; u_int8_t *p; u_int8_t *in; n = m; noff = off; p = mtod(n, u_int8_t *) + noff; in = &inbuf[0]; while (in - &inbuf[0] < 8) { if (!p) panic("mbuf chain?\n"); *in++ = *p++; noff++; if (noff < n->m_len) continue; do { n = n->m_next; } while (n && ! n->m_len); noff = 0; if (n) p = mtod(n, u_int8_t *) + noff; else p = NULL; } } in = &inbuf[0]; out = &outbuf[0]; n2l(in, tin0); tin[0] = tin0; n2l(in, tin1); tin[1] = tin1; BF_encrypt(tin, key, BF_DECRYPT); tout0 = tin[0] ^ xor0; tout1 = tin[1] ^ xor1; l2n(tout0, out); l2n(tout1, out); xor0 = tin0; xor1 = tin1; /* * copy the output buffer into the result. * need to update off and m. */ if (off + 8 < m->m_len) { bcopy(&outbuf[0], mtod(m, u_int8_t *) + off, 8); off += 8; } else if (off + 8 == m->m_len) { bcopy(&outbuf[0], mtod(m, u_int8_t *) + off, 8); do { m = m->m_next; } while (m && ! m->m_len); off = 0; } else { struct mbuf *n; size_t noff; u_int8_t *p; u_int8_t *out; n = m; noff = off; p = mtod(n, u_int8_t *) + noff; out = &outbuf[0]; while (out - &outbuf[0] < 8) { if (!p) panic("mbuf chain?\n"); *p++ = *out++; noff++; if (noff < n->m_len) continue; do { n = n->m_next; } while (n && ! n->m_len); noff = 0; if (n) p = mtod(n, u_int8_t *) + noff; else p = NULL; } m = n; off = noff; } length -= 8; } } + + return 0; } Index: head/sys/crypto/blowfish/bf_enc.c =================================================================== --- head/sys/crypto/blowfish/bf_enc.c (revision 62586) +++ head/sys/crypto/blowfish/bf_enc.c (revision 62587) @@ -1,142 +1,143 @@ +/* $FreeBSD$ */ +/* $KAME: bf_enc.c,v 1.3 2000/03/27 04:36:26 sumikawa Exp $ */ + /* crypto/bf/bf_enc.c */ /* Copyright (C) 1995-1997 Eric Young (eay@mincom.oz.au) * All rights reserved. * * This package is an SSL implementation written * by Eric Young (eay@mincom.oz.au). * The implementation was written so as to conform with Netscapes SSL. * * This library is free for commercial and non-commercial use as long as * the following conditions are aheared to. The following conditions * apply to all code found in this distribution, be it the RC4, RSA, * lhash, DES, etc., code; not just the SSL code. The SSL documentation * included with this distribution is covered by the same copyright terms * except that the holder is Tim Hudson (tjh@mincom.oz.au). * * Copyright remains Eric Young's, and as such any Copyright notices in * the code are not to be removed. * If this package is used in a product, Eric Young should be given attribution * as the author of the parts of the library used. * This can be in the form of a textual message at program startup or * in documentation (online or textual) provided with the package. * * 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 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 cryptographic software written by * Eric Young (eay@mincom.oz.au)" * The word 'cryptographic' can be left out if the rouines from the library * being used are not cryptographic related :-). * 4. If you include any Windows specific code (or a derivative thereof) from * the apps directory (application code) you must include an acknowledgement: * "This product includes software written by Tim Hudson (tjh@mincom.oz.au)" * * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * The licence and distribution terms for any publically available version or * derivative of this code cannot be changed. i.e. this code cannot simply be * copied and put under another distribution licence * [including the GNU Public Licence.] - * - * $FreeBSD$ */ #include #include /* Blowfish as implemented from 'Blowfish: Springer-Verlag paper' * (From LECTURE NOTES IN COIMPUTER SCIENCE 809, FAST SOFTWARE ENCRYPTION, * CAMBRIDGE SECURITY WORKSHOP, CAMBRIDGE, U.K., DECEMBER 9-11, 1993) */ #if (BF_ROUNDS != 16) && (BF_ROUNDS != 20) If you set BF_ROUNDS to some value other than 16 or 20, you will have to modify the code. #endif void BF_encrypt(data,key,encrypt) BF_LONG *data; BF_KEY *key; int encrypt; { register BF_LONG l,r,*p,*s; p=key->P; s= &(key->S[0]); l=data[0]; r=data[1]; if (encrypt) { l^=p[0]; BF_ENC(r,l,s,p[ 1]); BF_ENC(l,r,s,p[ 2]); BF_ENC(r,l,s,p[ 3]); BF_ENC(l,r,s,p[ 4]); BF_ENC(r,l,s,p[ 5]); BF_ENC(l,r,s,p[ 6]); BF_ENC(r,l,s,p[ 7]); BF_ENC(l,r,s,p[ 8]); BF_ENC(r,l,s,p[ 9]); BF_ENC(l,r,s,p[10]); BF_ENC(r,l,s,p[11]); BF_ENC(l,r,s,p[12]); BF_ENC(r,l,s,p[13]); BF_ENC(l,r,s,p[14]); BF_ENC(r,l,s,p[15]); BF_ENC(l,r,s,p[16]); #if BF_ROUNDS == 20 BF_ENC(r,l,s,p[17]); BF_ENC(l,r,s,p[18]); BF_ENC(r,l,s,p[19]); BF_ENC(l,r,s,p[20]); #endif r^=p[BF_ROUNDS+1]; } else { l^=p[BF_ROUNDS+1]; #if BF_ROUNDS == 20 BF_ENC(r,l,s,p[20]); BF_ENC(l,r,s,p[19]); BF_ENC(r,l,s,p[18]); BF_ENC(l,r,s,p[17]); #endif BF_ENC(r,l,s,p[16]); BF_ENC(l,r,s,p[15]); BF_ENC(r,l,s,p[14]); BF_ENC(l,r,s,p[13]); BF_ENC(r,l,s,p[12]); BF_ENC(l,r,s,p[11]); BF_ENC(r,l,s,p[10]); BF_ENC(l,r,s,p[ 9]); BF_ENC(r,l,s,p[ 8]); BF_ENC(l,r,s,p[ 7]); BF_ENC(r,l,s,p[ 6]); BF_ENC(l,r,s,p[ 5]); BF_ENC(r,l,s,p[ 4]); BF_ENC(l,r,s,p[ 3]); BF_ENC(r,l,s,p[ 2]); BF_ENC(l,r,s,p[ 1]); r^=p[0]; } data[1]=l&0xffffffff; data[0]=r&0xffffffff; } Index: head/sys/crypto/blowfish/bf_locl.h =================================================================== --- head/sys/crypto/blowfish/bf_locl.h (revision 62586) +++ head/sys/crypto/blowfish/bf_locl.h (revision 62587) @@ -1,217 +1,218 @@ +/* $FreeBSD$ */ +/* $KAME: bf_locl.h,v 1.3 2000/03/27 04:36:26 sumikawa Exp $ */ + /* crypto/bf/bf_local.h */ /* Copyright (C) 1995-1997 Eric Young (eay@mincom.oz.au) * All rights reserved. * * This package is an SSL implementation written * by Eric Young (eay@mincom.oz.au). * The implementation was written so as to conform with Netscapes SSL. * * This library is free for commercial and non-commercial use as long as * the following conditions are aheared to. The following conditions * apply to all code found in this distribution, be it the RC4, RSA, * lhash, DES, etc., code; not just the SSL code. The SSL documentation * included with this distribution is covered by the same copyright terms * except that the holder is Tim Hudson (tjh@mincom.oz.au). * * Copyright remains Eric Young's, and as such any Copyright notices in * the code are not to be removed. * If this package is used in a product, Eric Young should be given attribution * as the author of the parts of the library used. * This can be in the form of a textual message at program startup or * in documentation (online or textual) provided with the package. * * 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 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 cryptographic software written by * Eric Young (eay@mincom.oz.au)" * The word 'cryptographic' can be left out if the rouines from the library * being used are not cryptographic related :-). * 4. If you include any Windows specific code (or a derivative thereof) from * the apps directory (application code) you must include an acknowledgement: * "This product includes software written by Tim Hudson (tjh@mincom.oz.au)" * * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * The licence and distribution terms for any publically available version or * derivative of this code cannot be changed. i.e. this code cannot simply be * copied and put under another distribution licence * [including the GNU Public Licence.] - * - * $FreeBSD$ */ /* WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * * Always modify bf_locl.org since bf_locl.h is automatically generated from * it during SSLeay configuration. * * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING */ #undef c2l -#define c2l(c,l) (l =((unsigned long)(*((c)++))) , \ +#define c2l(c,l) (l =((unsigned long)(*((c)++))) , \ l|=((unsigned long)(*((c)++)))<< 8L, \ l|=((unsigned long)(*((c)++)))<<16L, \ l|=((unsigned long)(*((c)++)))<<24L) /* NOTE - c is not incremented as per c2l */ #undef c2ln -#define c2ln(c,l1,l2,n) { \ +#define c2ln(c,l1,l2,n) { \ c+=n; \ l1=l2=0; \ switch (n) { \ case 8: l2 =((unsigned long)(*(--(c))))<<24L; \ case 7: l2|=((unsigned long)(*(--(c))))<<16L; \ case 6: l2|=((unsigned long)(*(--(c))))<< 8L; \ case 5: l2|=((unsigned long)(*(--(c)))); \ case 4: l1 =((unsigned long)(*(--(c))))<<24L; \ case 3: l1|=((unsigned long)(*(--(c))))<<16L; \ case 2: l1|=((unsigned long)(*(--(c))))<< 8L; \ case 1: l1|=((unsigned long)(*(--(c)))); \ } \ } #undef l2c -#define l2c(l,c) (*((c)++)=(unsigned char)(((l) )&0xff), \ +#define l2c(l,c) (*((c)++)=(unsigned char)(((l) )&0xff), \ *((c)++)=(unsigned char)(((l)>> 8L)&0xff), \ *((c)++)=(unsigned char)(((l)>>16L)&0xff), \ *((c)++)=(unsigned char)(((l)>>24L)&0xff)) /* NOTE - c is not incremented as per l2c */ #undef l2cn -#define l2cn(l1,l2,c,n) { \ +#define l2cn(l1,l2,c,n) { \ c+=n; \ switch (n) { \ case 8: *(--(c))=(unsigned char)(((l2)>>24L)&0xff); \ case 7: *(--(c))=(unsigned char)(((l2)>>16L)&0xff); \ case 6: *(--(c))=(unsigned char)(((l2)>> 8L)&0xff); \ case 5: *(--(c))=(unsigned char)(((l2) )&0xff); \ case 4: *(--(c))=(unsigned char)(((l1)>>24L)&0xff); \ case 3: *(--(c))=(unsigned char)(((l1)>>16L)&0xff); \ case 2: *(--(c))=(unsigned char)(((l1)>> 8L)&0xff); \ case 1: *(--(c))=(unsigned char)(((l1) )&0xff); \ } \ } /* NOTE - c is not incremented as per n2l */ -#define n2ln(c,l1,l2,n) { \ +#define n2ln(c,l1,l2,n) { \ c+=n; \ l1=l2=0; \ switch (n) { \ case 8: l2 =((unsigned long)(*(--(c)))) ; \ case 7: l2|=((unsigned long)(*(--(c))))<< 8; \ case 6: l2|=((unsigned long)(*(--(c))))<<16; \ case 5: l2|=((unsigned long)(*(--(c))))<<24; \ case 4: l1 =((unsigned long)(*(--(c)))) ; \ case 3: l1|=((unsigned long)(*(--(c))))<< 8; \ case 2: l1|=((unsigned long)(*(--(c))))<<16; \ case 1: l1|=((unsigned long)(*(--(c))))<<24; \ } \ } /* NOTE - c is not incremented as per l2n */ -#define l2nn(l1,l2,c,n) { \ +#define l2nn(l1,l2,c,n) { \ c+=n; \ switch (n) { \ case 8: *(--(c))=(unsigned char)(((l2) )&0xff); \ case 7: *(--(c))=(unsigned char)(((l2)>> 8)&0xff); \ case 6: *(--(c))=(unsigned char)(((l2)>>16)&0xff); \ case 5: *(--(c))=(unsigned char)(((l2)>>24)&0xff); \ case 4: *(--(c))=(unsigned char)(((l1) )&0xff); \ case 3: *(--(c))=(unsigned char)(((l1)>> 8)&0xff); \ case 2: *(--(c))=(unsigned char)(((l1)>>16)&0xff); \ case 1: *(--(c))=(unsigned char)(((l1)>>24)&0xff); \ } \ } #undef n2l -#define n2l(c,l) (l =((unsigned long)(*((c)++)))<<24L, \ +#define n2l(c,l) (l =((unsigned long)(*((c)++)))<<24L, \ l|=((unsigned long)(*((c)++)))<<16L, \ l|=((unsigned long)(*((c)++)))<< 8L, \ l|=((unsigned long)(*((c)++)))) #undef l2n -#define l2n(l,c) (*((c)++)=(unsigned char)(((l)>>24L)&0xff), \ +#define l2n(l,c) (*((c)++)=(unsigned char)(((l)>>24L)&0xff), \ *((c)++)=(unsigned char)(((l)>>16L)&0xff), \ *((c)++)=(unsigned char)(((l)>> 8L)&0xff), \ *((c)++)=(unsigned char)(((l) )&0xff)) /* This is actually a big endian algorithm, the most significate byte * is used to lookup array 0 */ /* use BF_PTR2 for intel boxes, * BF_PTR for sparc and MIPS/SGI * use nothing for Alpha and HP. */ #if !defined(BF_PTR) && !defined(BF_PTR2) #undef BF_PTR #endif -#define BF_M 0x3fc -#define BF_0 22L -#define BF_1 14L -#define BF_2 6L -#define BF_3 2L /* left shift */ +#define BF_M 0x3fc +#define BF_0 22L +#define BF_1 14L +#define BF_2 6L +#define BF_3 2L /* left shift */ #if defined(BF_PTR2) /* This is basically a special pentium verson */ -#define BF_ENC(LL,R,S,P) \ +#define BF_ENC(LL,R,S,P) \ { \ BF_LONG t,u,v; \ u=R>>BF_0; \ v=R>>BF_1; \ u&=BF_M; \ v&=BF_M; \ t= *(BF_LONG *)((unsigned char *)&(S[ 0])+u); \ u=R>>BF_2; \ t+= *(BF_LONG *)((unsigned char *)&(S[256])+v); \ v=R<>BF_0)&BF_M))+ \ *(BF_LONG *)((unsigned char *)&(S[256])+((R>>BF_1)&BF_M)))^ \ *(BF_LONG *)((unsigned char *)&(S[512])+((R>>BF_2)&BF_M)))+ \ *(BF_LONG *)((unsigned char *)&(S[768])+((R<>24L) ] + \ S[0x0100+((R>>16L)&0xff)])^ \ S[0x0200+((R>> 8L)&0xff)])+ \ S[0x0300+((R )&0xff)])&0xffffffff; #endif Index: head/sys/crypto/blowfish/bf_pi.h =================================================================== --- head/sys/crypto/blowfish/bf_pi.h (revision 62586) +++ head/sys/crypto/blowfish/bf_pi.h (revision 62587) @@ -1,327 +1,328 @@ +/* $FreeBSD$ */ +/* $KAME: bf_pi.h,v 1.3 2000/03/27 04:36:26 sumikawa Exp $ */ + /* crypto/bf/bf_pi.h */ /* Copyright (C) 1995-1997 Eric Young (eay@mincom.oz.au) * All rights reserved. * * This package is an SSL implementation written * by Eric Young (eay@mincom.oz.au). * The implementation was written so as to conform with Netscapes SSL. * * This library is free for commercial and non-commercial use as long as * the following conditions are aheared to. The following conditions * apply to all code found in this distribution, be it the RC4, RSA, * lhash, DES, etc., code; not just the SSL code. The SSL documentation * included with this distribution is covered by the same copyright terms * except that the holder is Tim Hudson (tjh@mincom.oz.au). * * Copyright remains Eric Young's, and as such any Copyright notices in * the code are not to be removed. * If this package is used in a product, Eric Young should be given attribution * as the author of the parts of the library used. * This can be in the form of a textual message at program startup or * in documentation (online or textual) provided with the package. * * 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 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 cryptographic software written by * Eric Young (eay@mincom.oz.au)" * The word 'cryptographic' can be left out if the rouines from the library * being used are not cryptographic related :-). * 4. If you include any Windows specific code (or a derivative thereof) from * the apps directory (application code) you must include an acknowledgement: * "This product includes software written by Tim Hudson (tjh@mincom.oz.au)" * * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * The licence and distribution terms for any publically available version or * derivative of this code cannot be changed. i.e. this code cannot simply be * copied and put under another distribution licence * [including the GNU Public Licence.] - * - * $FreeBSD$ */ static BF_KEY bf_init= { { 0x243f6a88L, 0x85a308d3L, 0x13198a2eL, 0x03707344L, 0xa4093822L, 0x299f31d0L, 0x082efa98L, 0xec4e6c89L, 0x452821e6L, 0x38d01377L, 0xbe5466cfL, 0x34e90c6cL, 0xc0ac29b7L, 0xc97c50ddL, 0x3f84d5b5L, 0xb5470917L, 0x9216d5d9L, 0x8979fb1b },{ 0xd1310ba6L, 0x98dfb5acL, 0x2ffd72dbL, 0xd01adfb7L, 0xb8e1afedL, 0x6a267e96L, 0xba7c9045L, 0xf12c7f99L, 0x24a19947L, 0xb3916cf7L, 0x0801f2e2L, 0x858efc16L, 0x636920d8L, 0x71574e69L, 0xa458fea3L, 0xf4933d7eL, 0x0d95748fL, 0x728eb658L, 0x718bcd58L, 0x82154aeeL, 0x7b54a41dL, 0xc25a59b5L, 0x9c30d539L, 0x2af26013L, 0xc5d1b023L, 0x286085f0L, 0xca417918L, 0xb8db38efL, 0x8e79dcb0L, 0x603a180eL, 0x6c9e0e8bL, 0xb01e8a3eL, 0xd71577c1L, 0xbd314b27L, 0x78af2fdaL, 0x55605c60L, 0xe65525f3L, 0xaa55ab94L, 0x57489862L, 0x63e81440L, 0x55ca396aL, 0x2aab10b6L, 0xb4cc5c34L, 0x1141e8ceL, 0xa15486afL, 0x7c72e993L, 0xb3ee1411L, 0x636fbc2aL, 0x2ba9c55dL, 0x741831f6L, 0xce5c3e16L, 0x9b87931eL, 0xafd6ba33L, 0x6c24cf5cL, 0x7a325381L, 0x28958677L, 0x3b8f4898L, 0x6b4bb9afL, 0xc4bfe81bL, 0x66282193L, 0x61d809ccL, 0xfb21a991L, 0x487cac60L, 0x5dec8032L, 0xef845d5dL, 0xe98575b1L, 0xdc262302L, 0xeb651b88L, 0x23893e81L, 0xd396acc5L, 0x0f6d6ff3L, 0x83f44239L, 0x2e0b4482L, 0xa4842004L, 0x69c8f04aL, 0x9e1f9b5eL, 0x21c66842L, 0xf6e96c9aL, 0x670c9c61L, 0xabd388f0L, 0x6a51a0d2L, 0xd8542f68L, 0x960fa728L, 0xab5133a3L, 0x6eef0b6cL, 0x137a3be4L, 0xba3bf050L, 0x7efb2a98L, 0xa1f1651dL, 0x39af0176L, 0x66ca593eL, 0x82430e88L, 0x8cee8619L, 0x456f9fb4L, 0x7d84a5c3L, 0x3b8b5ebeL, 0xe06f75d8L, 0x85c12073L, 0x401a449fL, 0x56c16aa6L, 0x4ed3aa62L, 0x363f7706L, 0x1bfedf72L, 0x429b023dL, 0x37d0d724L, 0xd00a1248L, 0xdb0fead3L, 0x49f1c09bL, 0x075372c9L, 0x80991b7bL, 0x25d479d8L, 0xf6e8def7L, 0xe3fe501aL, 0xb6794c3bL, 0x976ce0bdL, 0x04c006baL, 0xc1a94fb6L, 0x409f60c4L, 0x5e5c9ec2L, 0x196a2463L, 0x68fb6fafL, 0x3e6c53b5L, 0x1339b2ebL, 0x3b52ec6fL, 0x6dfc511fL, 0x9b30952cL, 0xcc814544L, 0xaf5ebd09L, 0xbee3d004L, 0xde334afdL, 0x660f2807L, 0x192e4bb3L, 0xc0cba857L, 0x45c8740fL, 0xd20b5f39L, 0xb9d3fbdbL, 0x5579c0bdL, 0x1a60320aL, 0xd6a100c6L, 0x402c7279L, 0x679f25feL, 0xfb1fa3ccL, 0x8ea5e9f8L, 0xdb3222f8L, 0x3c7516dfL, 0xfd616b15L, 0x2f501ec8L, 0xad0552abL, 0x323db5faL, 0xfd238760L, 0x53317b48L, 0x3e00df82L, 0x9e5c57bbL, 0xca6f8ca0L, 0x1a87562eL, 0xdf1769dbL, 0xd542a8f6L, 0x287effc3L, 0xac6732c6L, 0x8c4f5573L, 0x695b27b0L, 0xbbca58c8L, 0xe1ffa35dL, 0xb8f011a0L, 0x10fa3d98L, 0xfd2183b8L, 0x4afcb56cL, 0x2dd1d35bL, 0x9a53e479L, 0xb6f84565L, 0xd28e49bcL, 0x4bfb9790L, 0xe1ddf2daL, 0xa4cb7e33L, 0x62fb1341L, 0xcee4c6e8L, 0xef20cadaL, 0x36774c01L, 0xd07e9efeL, 0x2bf11fb4L, 0x95dbda4dL, 0xae909198L, 0xeaad8e71L, 0x6b93d5a0L, 0xd08ed1d0L, 0xafc725e0L, 0x8e3c5b2fL, 0x8e7594b7L, 0x8ff6e2fbL, 0xf2122b64L, 0x8888b812L, 0x900df01cL, 0x4fad5ea0L, 0x688fc31cL, 0xd1cff191L, 0xb3a8c1adL, 0x2f2f2218L, 0xbe0e1777L, 0xea752dfeL, 0x8b021fa1L, 0xe5a0cc0fL, 0xb56f74e8L, 0x18acf3d6L, 0xce89e299L, 0xb4a84fe0L, 0xfd13e0b7L, 0x7cc43b81L, 0xd2ada8d9L, 0x165fa266L, 0x80957705L, 0x93cc7314L, 0x211a1477L, 0xe6ad2065L, 0x77b5fa86L, 0xc75442f5L, 0xfb9d35cfL, 0xebcdaf0cL, 0x7b3e89a0L, 0xd6411bd3L, 0xae1e7e49L, 0x00250e2dL, 0x2071b35eL, 0x226800bbL, 0x57b8e0afL, 0x2464369bL, 0xf009b91eL, 0x5563911dL, 0x59dfa6aaL, 0x78c14389L, 0xd95a537fL, 0x207d5ba2L, 0x02e5b9c5L, 0x83260376L, 0x6295cfa9L, 0x11c81968L, 0x4e734a41L, 0xb3472dcaL, 0x7b14a94aL, 0x1b510052L, 0x9a532915L, 0xd60f573fL, 0xbc9bc6e4L, 0x2b60a476L, 0x81e67400L, 0x08ba6fb5L, 0x571be91fL, 0xf296ec6bL, 0x2a0dd915L, 0xb6636521L, 0xe7b9f9b6L, 0xff34052eL, 0xc5855664L, 0x53b02d5dL, 0xa99f8fa1L, 0x08ba4799L, 0x6e85076aL, 0x4b7a70e9L, 0xb5b32944L, 0xdb75092eL, 0xc4192623L, 0xad6ea6b0L, 0x49a7df7dL, 0x9cee60b8L, 0x8fedb266L, 0xecaa8c71L, 0x699a17ffL, 0x5664526cL, 0xc2b19ee1L, 0x193602a5L, 0x75094c29L, 0xa0591340L, 0xe4183a3eL, 0x3f54989aL, 0x5b429d65L, 0x6b8fe4d6L, 0x99f73fd6L, 0xa1d29c07L, 0xefe830f5L, 0x4d2d38e6L, 0xf0255dc1L, 0x4cdd2086L, 0x8470eb26L, 0x6382e9c6L, 0x021ecc5eL, 0x09686b3fL, 0x3ebaefc9L, 0x3c971814L, 0x6b6a70a1L, 0x687f3584L, 0x52a0e286L, 0xb79c5305L, 0xaa500737L, 0x3e07841cL, 0x7fdeae5cL, 0x8e7d44ecL, 0x5716f2b8L, 0xb03ada37L, 0xf0500c0dL, 0xf01c1f04L, 0x0200b3ffL, 0xae0cf51aL, 0x3cb574b2L, 0x25837a58L, 0xdc0921bdL, 0xd19113f9L, 0x7ca92ff6L, 0x94324773L, 0x22f54701L, 0x3ae5e581L, 0x37c2dadcL, 0xc8b57634L, 0x9af3dda7L, 0xa9446146L, 0x0fd0030eL, 0xecc8c73eL, 0xa4751e41L, 0xe238cd99L, 0x3bea0e2fL, 0x3280bba1L, 0x183eb331L, 0x4e548b38L, 0x4f6db908L, 0x6f420d03L, 0xf60a04bfL, 0x2cb81290L, 0x24977c79L, 0x5679b072L, 0xbcaf89afL, 0xde9a771fL, 0xd9930810L, 0xb38bae12L, 0xdccf3f2eL, 0x5512721fL, 0x2e6b7124L, 0x501adde6L, 0x9f84cd87L, 0x7a584718L, 0x7408da17L, 0xbc9f9abcL, 0xe94b7d8cL, 0xec7aec3aL, 0xdb851dfaL, 0x63094366L, 0xc464c3d2L, 0xef1c1847L, 0x3215d908L, 0xdd433b37L, 0x24c2ba16L, 0x12a14d43L, 0x2a65c451L, 0x50940002L, 0x133ae4ddL, 0x71dff89eL, 0x10314e55L, 0x81ac77d6L, 0x5f11199bL, 0x043556f1L, 0xd7a3c76bL, 0x3c11183bL, 0x5924a509L, 0xf28fe6edL, 0x97f1fbfaL, 0x9ebabf2cL, 0x1e153c6eL, 0x86e34570L, 0xeae96fb1L, 0x860e5e0aL, 0x5a3e2ab3L, 0x771fe71cL, 0x4e3d06faL, 0x2965dcb9L, 0x99e71d0fL, 0x803e89d6L, 0x5266c825L, 0x2e4cc978L, 0x9c10b36aL, 0xc6150ebaL, 0x94e2ea78L, 0xa5fc3c53L, 0x1e0a2df4L, 0xf2f74ea7L, 0x361d2b3dL, 0x1939260fL, 0x19c27960L, 0x5223a708L, 0xf71312b6L, 0xebadfe6eL, 0xeac31f66L, 0xe3bc4595L, 0xa67bc883L, 0xb17f37d1L, 0x018cff28L, 0xc332ddefL, 0xbe6c5aa5L, 0x65582185L, 0x68ab9802L, 0xeecea50fL, 0xdb2f953bL, 0x2aef7dadL, 0x5b6e2f84L, 0x1521b628L, 0x29076170L, 0xecdd4775L, 0x619f1510L, 0x13cca830L, 0xeb61bd96L, 0x0334fe1eL, 0xaa0363cfL, 0xb5735c90L, 0x4c70a239L, 0xd59e9e0bL, 0xcbaade14L, 0xeecc86bcL, 0x60622ca7L, 0x9cab5cabL, 0xb2f3846eL, 0x648b1eafL, 0x19bdf0caL, 0xa02369b9L, 0x655abb50L, 0x40685a32L, 0x3c2ab4b3L, 0x319ee9d5L, 0xc021b8f7L, 0x9b540b19L, 0x875fa099L, 0x95f7997eL, 0x623d7da8L, 0xf837889aL, 0x97e32d77L, 0x11ed935fL, 0x16681281L, 0x0e358829L, 0xc7e61fd6L, 0x96dedfa1L, 0x7858ba99L, 0x57f584a5L, 0x1b227263L, 0x9b83c3ffL, 0x1ac24696L, 0xcdb30aebL, 0x532e3054L, 0x8fd948e4L, 0x6dbc3128L, 0x58ebf2efL, 0x34c6ffeaL, 0xfe28ed61L, 0xee7c3c73L, 0x5d4a14d9L, 0xe864b7e3L, 0x42105d14L, 0x203e13e0L, 0x45eee2b6L, 0xa3aaabeaL, 0xdb6c4f15L, 0xfacb4fd0L, 0xc742f442L, 0xef6abbb5L, 0x654f3b1dL, 0x41cd2105L, 0xd81e799eL, 0x86854dc7L, 0xe44b476aL, 0x3d816250L, 0xcf62a1f2L, 0x5b8d2646L, 0xfc8883a0L, 0xc1c7b6a3L, 0x7f1524c3L, 0x69cb7492L, 0x47848a0bL, 0x5692b285L, 0x095bbf00L, 0xad19489dL, 0x1462b174L, 0x23820e00L, 0x58428d2aL, 0x0c55f5eaL, 0x1dadf43eL, 0x233f7061L, 0x3372f092L, 0x8d937e41L, 0xd65fecf1L, 0x6c223bdbL, 0x7cde3759L, 0xcbee7460L, 0x4085f2a7L, 0xce77326eL, 0xa6078084L, 0x19f8509eL, 0xe8efd855L, 0x61d99735L, 0xa969a7aaL, 0xc50c06c2L, 0x5a04abfcL, 0x800bcadcL, 0x9e447a2eL, 0xc3453484L, 0xfdd56705L, 0x0e1e9ec9L, 0xdb73dbd3L, 0x105588cdL, 0x675fda79L, 0xe3674340L, 0xc5c43465L, 0x713e38d8L, 0x3d28f89eL, 0xf16dff20L, 0x153e21e7L, 0x8fb03d4aL, 0xe6e39f2bL, 0xdb83adf7L, 0xe93d5a68L, 0x948140f7L, 0xf64c261cL, 0x94692934L, 0x411520f7L, 0x7602d4f7L, 0xbcf46b2eL, 0xd4a20068L, 0xd4082471L, 0x3320f46aL, 0x43b7d4b7L, 0x500061afL, 0x1e39f62eL, 0x97244546L, 0x14214f74L, 0xbf8b8840L, 0x4d95fc1dL, 0x96b591afL, 0x70f4ddd3L, 0x66a02f45L, 0xbfbc09ecL, 0x03bd9785L, 0x7fac6dd0L, 0x31cb8504L, 0x96eb27b3L, 0x55fd3941L, 0xda2547e6L, 0xabca0a9aL, 0x28507825L, 0x530429f4L, 0x0a2c86daL, 0xe9b66dfbL, 0x68dc1462L, 0xd7486900L, 0x680ec0a4L, 0x27a18deeL, 0x4f3ffea2L, 0xe887ad8cL, 0xb58ce006L, 0x7af4d6b6L, 0xaace1e7cL, 0xd3375fecL, 0xce78a399L, 0x406b2a42L, 0x20fe9e35L, 0xd9f385b9L, 0xee39d7abL, 0x3b124e8bL, 0x1dc9faf7L, 0x4b6d1856L, 0x26a36631L, 0xeae397b2L, 0x3a6efa74L, 0xdd5b4332L, 0x6841e7f7L, 0xca7820fbL, 0xfb0af54eL, 0xd8feb397L, 0x454056acL, 0xba489527L, 0x55533a3aL, 0x20838d87L, 0xfe6ba9b7L, 0xd096954bL, 0x55a867bcL, 0xa1159a58L, 0xcca92963L, 0x99e1db33L, 0xa62a4a56L, 0x3f3125f9L, 0x5ef47e1cL, 0x9029317cL, 0xfdf8e802L, 0x04272f70L, 0x80bb155cL, 0x05282ce3L, 0x95c11548L, 0xe4c66d22L, 0x48c1133fL, 0xc70f86dcL, 0x07f9c9eeL, 0x41041f0fL, 0x404779a4L, 0x5d886e17L, 0x325f51ebL, 0xd59bc0d1L, 0xf2bcc18fL, 0x41113564L, 0x257b7834L, 0x602a9c60L, 0xdff8e8a3L, 0x1f636c1bL, 0x0e12b4c2L, 0x02e1329eL, 0xaf664fd1L, 0xcad18115L, 0x6b2395e0L, 0x333e92e1L, 0x3b240b62L, 0xeebeb922L, 0x85b2a20eL, 0xe6ba0d99L, 0xde720c8cL, 0x2da2f728L, 0xd0127845L, 0x95b794fdL, 0x647d0862L, 0xe7ccf5f0L, 0x5449a36fL, 0x877d48faL, 0xc39dfd27L, 0xf33e8d1eL, 0x0a476341L, 0x992eff74L, 0x3a6f6eabL, 0xf4f8fd37L, 0xa812dc60L, 0xa1ebddf8L, 0x991be14cL, 0xdb6e6b0dL, 0xc67b5510L, 0x6d672c37L, 0x2765d43bL, 0xdcd0e804L, 0xf1290dc7L, 0xcc00ffa3L, 0xb5390f92L, 0x690fed0bL, 0x667b9ffbL, 0xcedb7d9cL, 0xa091cf0bL, 0xd9155ea3L, 0xbb132f88L, 0x515bad24L, 0x7b9479bfL, 0x763bd6ebL, 0x37392eb3L, 0xcc115979L, 0x8026e297L, 0xf42e312dL, 0x6842ada7L, 0xc66a2b3bL, 0x12754cccL, 0x782ef11cL, 0x6a124237L, 0xb79251e7L, 0x06a1bbe6L, 0x4bfb6350L, 0x1a6b1018L, 0x11caedfaL, 0x3d25bdd8L, 0xe2e1c3c9L, 0x44421659L, 0x0a121386L, 0xd90cec6eL, 0xd5abea2aL, 0x64af674eL, 0xda86a85fL, 0xbebfe988L, 0x64e4c3feL, 0x9dbc8057L, 0xf0f7c086L, 0x60787bf8L, 0x6003604dL, 0xd1fd8346L, 0xf6381fb0L, 0x7745ae04L, 0xd736fcccL, 0x83426b33L, 0xf01eab71L, 0xb0804187L, 0x3c005e5fL, 0x77a057beL, 0xbde8ae24L, 0x55464299L, 0xbf582e61L, 0x4e58f48fL, 0xf2ddfda2L, 0xf474ef38L, 0x8789bdc2L, 0x5366f9c3L, 0xc8b38e74L, 0xb475f255L, 0x46fcd9b9L, 0x7aeb2661L, 0x8b1ddf84L, 0x846a0e79L, 0x915f95e2L, 0x466e598eL, 0x20b45770L, 0x8cd55591L, 0xc902de4cL, 0xb90bace1L, 0xbb8205d0L, 0x11a86248L, 0x7574a99eL, 0xb77f19b6L, 0xe0a9dc09L, 0x662d09a1L, 0xc4324633L, 0xe85a1f02L, 0x09f0be8cL, 0x4a99a025L, 0x1d6efe10L, 0x1ab93d1dL, 0x0ba5a4dfL, 0xa186f20fL, 0x2868f169L, 0xdcb7da83L, 0x573906feL, 0xa1e2ce9bL, 0x4fcd7f52L, 0x50115e01L, 0xa70683faL, 0xa002b5c4L, 0x0de6d027L, 0x9af88c27L, 0x773f8641L, 0xc3604c06L, 0x61a806b5L, 0xf0177a28L, 0xc0f586e0L, 0x006058aaL, 0x30dc7d62L, 0x11e69ed7L, 0x2338ea63L, 0x53c2dd94L, 0xc2c21634L, 0xbbcbee56L, 0x90bcb6deL, 0xebfc7da1L, 0xce591d76L, 0x6f05e409L, 0x4b7c0188L, 0x39720a3dL, 0x7c927c24L, 0x86e3725fL, 0x724d9db9L, 0x1ac15bb4L, 0xd39eb8fcL, 0xed545578L, 0x08fca5b5L, 0xd83d7cd3L, 0x4dad0fc4L, 0x1e50ef5eL, 0xb161e6f8L, 0xa28514d9L, 0x6c51133cL, 0x6fd5c7e7L, 0x56e14ec4L, 0x362abfceL, 0xddc6c837L, 0xd79a3234L, 0x92638212L, 0x670efa8eL, 0x406000e0L, 0x3a39ce37L, 0xd3faf5cfL, 0xabc27737L, 0x5ac52d1bL, 0x5cb0679eL, 0x4fa33742L, 0xd3822740L, 0x99bc9bbeL, 0xd5118e9dL, 0xbf0f7315L, 0xd62d1c7eL, 0xc700c47bL, 0xb78c1b6bL, 0x21a19045L, 0xb26eb1beL, 0x6a366eb4L, 0x5748ab2fL, 0xbc946e79L, 0xc6a376d2L, 0x6549c2c8L, 0x530ff8eeL, 0x468dde7dL, 0xd5730a1dL, 0x4cd04dc6L, 0x2939bbdbL, 0xa9ba4650L, 0xac9526e8L, 0xbe5ee304L, 0xa1fad5f0L, 0x6a2d519aL, 0x63ef8ce2L, 0x9a86ee22L, 0xc089c2b8L, 0x43242ef6L, 0xa51e03aaL, 0x9cf2d0a4L, 0x83c061baL, 0x9be96a4dL, 0x8fe51550L, 0xba645bd6L, 0x2826a2f9L, 0xa73a3ae1L, 0x4ba99586L, 0xef5562e9L, 0xc72fefd3L, 0xf752f7daL, 0x3f046f69L, 0x77fa0a59L, 0x80e4a915L, 0x87b08601L, 0x9b09e6adL, 0x3b3ee593L, 0xe990fd5aL, 0x9e34d797L, 0x2cf0b7d9L, 0x022b8b51L, 0x96d5ac3aL, 0x017da67dL, 0xd1cf3ed6L, 0x7c7d2d28L, 0x1f9f25cfL, 0xadf2b89bL, 0x5ad6b472L, 0x5a88f54cL, 0xe029ac71L, 0xe019a5e6L, 0x47b0acfdL, 0xed93fa9bL, 0xe8d3c48dL, 0x283b57ccL, 0xf8d56629L, 0x79132e28L, 0x785f0191L, 0xed756055L, 0xf7960e44L, 0xe3d35e8cL, 0x15056dd4L, 0x88f46dbaL, 0x03a16125L, 0x0564f0bdL, 0xc3eb9e15L, 0x3c9057a2L, 0x97271aecL, 0xa93a072aL, 0x1b3f6d9bL, 0x1e6321f5L, 0xf59c66fbL, 0x26dcf319L, 0x7533d928L, 0xb155fdf5L, 0x03563482L, 0x8aba3cbbL, 0x28517711L, 0xc20ad9f8L, 0xabcc5167L, 0xccad925fL, 0x4de81751L, 0x3830dc8eL, 0x379d5862L, 0x9320f991L, 0xea7a90c2L, 0xfb3e7bceL, 0x5121ce64L, 0x774fbe32L, 0xa8b6e37eL, 0xc3293d46L, 0x48de5369L, 0x6413e680L, 0xa2ae0810L, 0xdd6db224L, 0x69852dfdL, 0x09072166L, 0xb39a460aL, 0x6445c0ddL, 0x586cdecfL, 0x1c20c8aeL, 0x5bbef7ddL, 0x1b588d40L, 0xccd2017fL, 0x6bb4e3bbL, 0xdda26a7eL, 0x3a59ff45L, 0x3e350a44L, 0xbcb4cdd5L, 0x72eacea8L, 0xfa6484bbL, 0x8d6612aeL, 0xbf3c6f47L, 0xd29be463L, 0x542f5d9eL, 0xaec2771bL, 0xf64e6370L, 0x740e0d8dL, 0xe75b1357L, 0xf8721671L, 0xaf537d5dL, 0x4040cb08L, 0x4eb4e2ccL, 0x34d2466aL, 0x0115af84L, 0xe1b00428L, 0x95983a1dL, 0x06b89fb4L, 0xce6ea048L, 0x6f3f3b82L, 0x3520ab82L, 0x011a1d4bL, 0x277227f8L, 0x611560b1L, 0xe7933fdcL, 0xbb3a792bL, 0x344525bdL, 0xa08839e1L, 0x51ce794bL, 0x2f32c9b7L, 0xa01fbac9L, 0xe01cc87eL, 0xbcc7d1f6L, 0xcf0111c3L, 0xa1e8aac7L, 0x1a908749L, 0xd44fbd9aL, 0xd0dadecbL, 0xd50ada38L, 0x0339c32aL, 0xc6913667L, 0x8df9317cL, 0xe0b12b4fL, 0xf79e59b7L, 0x43f5bb3aL, 0xf2d519ffL, 0x27d9459cL, 0xbf97222cL, 0x15e6fc2aL, 0x0f91fc71L, 0x9b941525L, 0xfae59361L, 0xceb69cebL, 0xc2a86459L, 0x12baa8d1L, 0xb6c1075eL, 0xe3056a0cL, 0x10d25065L, 0xcb03a442L, 0xe0ec6e0eL, 0x1698db3bL, 0x4c98a0beL, 0x3278e964L, 0x9f1f9532L, 0xe0d392dfL, 0xd3a0342bL, 0x8971f21eL, 0x1b0a7441L, 0x4ba3348cL, 0xc5be7120L, 0xc37632d8L, 0xdf359f8dL, 0x9b992f2eL, 0xe60b6f47L, 0x0fe3f11dL, 0xe54cda54L, 0x1edad891L, 0xce6279cfL, 0xcd3e7e6fL, 0x1618b166L, 0xfd2c1d05L, 0x848fd2c5L, 0xf6fb2299L, 0xf523f357L, 0xa6327623L, 0x93a83531L, 0x56cccd02L, 0xacf08162L, 0x5a75ebb5L, 0x6e163697L, 0x88d273ccL, 0xde966292L, 0x81b949d0L, 0x4c50901bL, 0x71c65614L, 0xe6c6c7bdL, 0x327a140aL, 0x45e1d006L, 0xc3f27b9aL, 0xc9aa53fdL, 0x62a80f00L, 0xbb25bfe2L, 0x35bdd2f6L, 0x71126905L, 0xb2040222L, 0xb6cbcf7cL, 0xcd769c2bL, 0x53113ec0L, 0x1640e3d3L, 0x38abbd60L, 0x2547adf0L, 0xba38209cL, 0xf746ce76L, 0x77afa1c5L, 0x20756060L, 0x85cbfe4eL, 0x8ae88dd8L, 0x7aaaf9b0L, 0x4cf9aa7eL, 0x1948c25cL, 0x02fb8a8cL, 0x01c36ae4L, 0xd6ebe1f9L, 0x90d4f869L, 0xa65cdea0L, 0x3f09252dL, 0xc208e69fL, 0xb74e6132L, 0xce77e25bL, 0x578fdfe3L, 0x3ac372e6L, } }; Index: head/sys/crypto/blowfish/bf_skey.c =================================================================== --- head/sys/crypto/blowfish/bf_skey.c (revision 62586) +++ head/sys/crypto/blowfish/bf_skey.c (revision 62587) @@ -1,122 +1,123 @@ +/* $FreeBSD$ */ +/* $KAME: bf_skey.c,v 1.3 2000/03/27 04:36:27 sumikawa Exp $ */ + /* crypto/bf/bf_skey.c */ /* Copyright (C) 1995-1997 Eric Young (eay@mincom.oz.au) * All rights reserved. * * This package is an SSL implementation written * by Eric Young (eay@mincom.oz.au). * The implementation was written so as to conform with Netscapes SSL. * * This library is free for commercial and non-commercial use as long as * the following conditions are aheared to. The following conditions * apply to all code found in this distribution, be it the RC4, RSA, * lhash, DES, etc., code; not just the SSL code. The SSL documentation * included with this distribution is covered by the same copyright terms * except that the holder is Tim Hudson (tjh@mincom.oz.au). * * Copyright remains Eric Young's, and as such any Copyright notices in * the code are not to be removed. * If this package is used in a product, Eric Young should be given attribution * as the author of the parts of the library used. * This can be in the form of a textual message at program startup or * in documentation (online or textual) provided with the package. * * 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 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 cryptographic software written by * Eric Young (eay@mincom.oz.au)" * The word 'cryptographic' can be left out if the rouines from the library * being used are not cryptographic related :-). * 4. If you include any Windows specific code (or a derivative thereof) from * the apps directory (application code) you must include an acknowledgement: * "This product includes software written by Tim Hudson (tjh@mincom.oz.au)" * * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * The licence and distribution terms for any publically available version or * derivative of this code cannot be changed. i.e. this code cannot simply be * copied and put under another distribution licence * [including the GNU Public Licence.] - * - * $FreeBSD$ */ #include #include #include #include #include #include void BF_set_key(key,len,data) BF_KEY *key; int len; unsigned char *data; { int i; BF_LONG *p,ri,in[2]; unsigned char *d,*end; memcpy((char *)key,(char *)&bf_init,sizeof(BF_KEY)); p=key->P; if (len > ((BF_ROUNDS+2)*4)) len=(BF_ROUNDS+2)*4; d=data; end= &(data[len]); for (i=0; i<(BF_ROUNDS+2); i++) { ri= *(d++); if (d >= end) d=data; ri<<=8; ri|= *(d++); if (d >= end) d=data; ri<<=8; ri|= *(d++); if (d >= end) d=data; ri<<=8; ri|= *(d++); if (d >= end) d=data; p[i]^=ri; } in[0]=0L; in[1]=0L; for (i=0; i<(BF_ROUNDS+2); i+=2) { BF_encrypt(in,key,BF_ENCRYPT); p[i ]=in[0]; p[i+1]=in[1]; } p=key->S; for (i=0; i<4*256; i+=2) { BF_encrypt(in,key,BF_ENCRYPT); p[i ]=in[0]; p[i+1]=in[1]; } } Index: head/sys/crypto/blowfish/blowfish.h =================================================================== --- head/sys/crypto/blowfish/blowfish.h (revision 62586) +++ head/sys/crypto/blowfish/blowfish.h (revision 62587) @@ -1,124 +1,124 @@ +/* $FreeBSD$ */ +/* $KAME: blowfish.h,v 1.4 2000/06/14 10:41:16 itojun Exp $ */ + /* crypto/bf/blowfish.h */ /* Copyright (C) 1995-1997 Eric Young (eay@mincom.oz.au) * All rights reserved. * * This package is an SSL implementation written * by Eric Young (eay@mincom.oz.au). * The implementation was written so as to conform with Netscapes SSL. * * This library is free for commercial and non-commercial use as long as * the following conditions are aheared to. The following conditions * apply to all code found in this distribution, be it the RC4, RSA, * lhash, DES, etc., code; not just the SSL code. The SSL documentation * included with this distribution is covered by the same copyright terms * except that the holder is Tim Hudson (tjh@mincom.oz.au). * * Copyright remains Eric Young's, and as such any Copyright notices in * the code are not to be removed. * If this package is used in a product, Eric Young should be given attribution * as the author of the parts of the library used. * This can be in the form of a textual message at program startup or * in documentation (online or textual) provided with the package. * * 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 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 cryptographic software written by * Eric Young (eay@mincom.oz.au)" * The word 'cryptographic' can be left out if the rouines from the library * being used are not cryptographic related :-). * 4. If you include any Windows specific code (or a derivative thereof) from * the apps directory (application code) you must include an acknowledgement: * "This product includes software written by Tim Hudson (tjh@mincom.oz.au)" * * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * The licence and distribution terms for any publically available version or * derivative of this code cannot be changed. i.e. this code cannot simply be * copied and put under another distribution licence * [including the GNU Public Licence.] - * - * $FreeBSD$ */ #ifndef HEADER_BLOWFISH_H -#define HEADER_BLOWFISH_H +#define HEADER_BLOWFISH_H #ifdef __cplusplus extern "C" { #endif -#define BF_ENCRYPT 1 -#define BF_DECRYPT 0 +#define BF_ENCRYPT 1 +#define BF_DECRYPT 0 /* If you make this 'unsigned int' the pointer variants will work on * the Alpha, otherwise they will not. Strangly using the '8 byte' * BF_LONG and the default 'non-pointer' inner loop is the best configuration * for the Alpha */ -#define BF_LONG unsigned long +#define BF_LONG unsigned long -#define BF_ROUNDS 16 -#define BF_BLOCK 8 +#define BF_ROUNDS 16 +#define BF_BLOCK 8 typedef struct bf_key_st { BF_LONG P[BF_ROUNDS+2]; BF_LONG S[4*256]; } BF_KEY; #ifndef NOPROTO void BF_set_key(BF_KEY *key, int len, unsigned char *data); void BF_ecb_encrypt(unsigned char *in,unsigned char *out,BF_KEY *key, int encrypt); void BF_encrypt(BF_LONG *data,BF_KEY *key,int encrypt); void BF_cbc_encrypt(unsigned char *in, unsigned char *out, long length, BF_KEY *ks, unsigned char *iv, int encrypt); void BF_cfb64_encrypt(unsigned char *in, unsigned char *out, long length, BF_KEY *schedule, unsigned char *ivec, int *num, int encrypt); void BF_ofb64_encrypt(unsigned char *in, unsigned char *out, long length, BF_KEY *schedule, unsigned char *ivec, int *num); char *BF_options(void); /* added by itojun */ struct mbuf; -void BF_cbc_encrypt_m(struct mbuf *, int, int, BF_KEY *, - unsigned char *, int); +int BF_cbc_encrypt_m(struct mbuf *, int, int, BF_KEY *, unsigned char *, int); #else void BF_set_key(); void BF_ecb_encrypt(); void BF_encrypt(); void BF_cbc_encrypt(); void BF_cfb64_encrypt(); void BF_ofb64_encrypt(); char *BF_options(); /* added by itojun */ void BF_cbc_encrypt_m(); #endif #ifdef __cplusplus } #endif #endif Index: head/sys/crypto/cast128/cast128.c =================================================================== --- head/sys/crypto/cast128/cast128.c (revision 62586) +++ head/sys/crypto/cast128/cast128.c (revision 62587) @@ -1,873 +1,874 @@ +/* $FreeBSD$ */ +/* $KAME: cast128.c,v 1.3 2000/03/27 04:36:29 sumikawa Exp $ */ + /* * heavily modified by Tomomi Suzuki */ /* * The CAST-128 Encryption Algorithm (RFC 2144) * * original implementation * 1997/08/21 */ /* * Copyright (C) 1997 Hideo "Sir MANMOS" Morishita * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY Hideo "Sir MaNMOS" Morishita ``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 Hideo "Sir MaNMOS" Morishita BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ #include #include #include #include static u_int32_t S1[]; static u_int32_t S2[]; static u_int32_t S3[]; static u_int32_t S4[]; static u_int32_t S5[]; static u_int32_t S6[]; static u_int32_t S7[]; static u_int32_t S8[]; /* * Step 1 */ void set_cast128_subkey(u_int32_t *subkey, u_int8_t *key) { u_int32_t buf[8]; /* for x0x1x2x3, x4x5x6x7 ..., z0z1z2z3, ... */ buf[0] = (key[ 0] << 24) | (key[ 1] << 16) | (key[ 2] << 8) | key[ 3]; buf[1] = (key[ 4] << 24) | (key[ 5] << 16) | (key[ 6] << 8) | key[ 7]; buf[2] = (key[ 8] << 24) | (key[ 9] << 16) | (key[10] << 8) | key[11]; buf[3] = (key[12] << 24) | (key[13] << 16) | (key[14] << 8) | key[15]; /* masking subkey */ z0z1z2z3 = x0x1x2x3 ^ S5[xD] ^ S6[xF] ^ S7[xC] ^ S8[xE] ^ S7[x8]; z4z5z6z7 = x8x9xAxB ^ S5[z0] ^ S6[z2] ^ S7[z1] ^ S8[z3] ^ S8[xA]; z8z9zAzB = xCxDxExF ^ S5[z7] ^ S6[z6] ^ S7[z5] ^ S8[z4] ^ S5[x9]; zCzDzEzF = x4x5x6x7 ^ S5[zA] ^ S6[z9] ^ S7[zB] ^ S8[z8] ^ S6[xB]; subkey[0] = S5[z8] ^ S6[z9] ^ S7[z7] ^ S8[z6] ^ S5[z2]; subkey[1] = S5[zA] ^ S6[zB] ^ S7[z5] ^ S8[z4] ^ S6[z6]; subkey[2] = S5[zC] ^ S6[zD] ^ S7[z3] ^ S8[z2] ^ S7[z9]; subkey[3] = S5[zE] ^ S6[zF] ^ S7[z1] ^ S8[z0] ^ S8[zC]; x0x1x2x3 = z8z9zAzB ^ S5[z5] ^ S6[z7] ^ S7[z4] ^ S8[z6] ^ S7[z0]; x4x5x6x7 = z0z1z2z3 ^ S5[x0] ^ S6[x2] ^ S7[x1] ^ S8[x3] ^ S8[z2]; x8x9xAxB = z4z5z6z7 ^ S5[x7] ^ S6[x6] ^ S7[x5] ^ S8[x4] ^ S5[z1]; xCxDxExF = zCzDzEzF ^ S5[xA] ^ S6[x9] ^ S7[xB] ^ S8[x8] ^ S6[z3]; subkey[4] = S5[x3] ^ S6[x2] ^ S7[xC] ^ S8[xD] ^ S5[x8]; subkey[5] = S5[x1] ^ S6[x0] ^ S7[xE] ^ S8[xF] ^ S6[xD]; subkey[6] = S5[x7] ^ S6[x6] ^ S7[x8] ^ S8[x9] ^ S7[x3]; subkey[7] = S5[x5] ^ S6[x4] ^ S7[xA] ^ S8[xB] ^ S8[x7]; z0z1z2z3 = x0x1x2x3 ^ S5[xD] ^ S6[xF] ^ S7[xC] ^ S8[xE] ^ S7[x8]; z4z5z6z7 = x8x9xAxB ^ S5[z0] ^ S6[z2] ^ S7[z1] ^ S8[z3] ^ S8[xA]; z8z9zAzB = xCxDxExF ^ S5[z7] ^ S6[z6] ^ S7[z5] ^ S8[z4] ^ S5[x9]; zCzDzEzF = x4x5x6x7 ^ S5[zA] ^ S6[z9] ^ S7[zB] ^ S8[z8] ^ S6[xB]; subkey[8] = S5[z3] ^ S6[z2] ^ S7[zC] ^ S8[zD] ^ S5[z9]; subkey[9] = S5[z1] ^ S6[z0] ^ S7[zE] ^ S8[zF] ^ S6[zC]; subkey[10] = S5[z7] ^ S6[z6] ^ S7[z8] ^ S8[z9] ^ S7[z2]; subkey[11] = S5[z5] ^ S6[z4] ^ S7[zA] ^ S8[zB] ^ S8[z6]; x0x1x2x3 = z8z9zAzB ^ S5[z5] ^ S6[z7] ^ S7[z4] ^ S8[z6] ^ S7[z0]; x4x5x6x7 = z0z1z2z3 ^ S5[x0] ^ S6[x2] ^ S7[x1] ^ S8[x3] ^ S8[z2]; x8x9xAxB = z4z5z6z7 ^ S5[x7] ^ S6[x6] ^ S7[x5] ^ S8[x4] ^ S5[z1]; xCxDxExF = zCzDzEzF ^ S5[xA] ^ S6[x9] ^ S7[xB] ^ S8[x8] ^ S6[z3]; subkey[12] = S5[x8] ^ S6[x9] ^ S7[x7] ^ S8[x6] ^ S5[x3]; subkey[13] = S5[xA] ^ S6[xB] ^ S7[x5] ^ S8[x4] ^ S6[x7]; subkey[14] = S5[xC] ^ S6[xD] ^ S7[x3] ^ S8[x2] ^ S7[x8]; subkey[15] = S5[xE] ^ S6[xF] ^ S7[x1] ^ S8[x0] ^ S8[xD]; /* rotate subkey (least significast 5 bits) */ z0z1z2z3 = x0x1x2x3 ^ S5[xD] ^ S6[xF] ^ S7[xC] ^ S8[xE] ^ S7[x8]; z4z5z6z7 = x8x9xAxB ^ S5[z0] ^ S6[z2] ^ S7[z1] ^ S8[z3] ^ S8[xA]; z8z9zAzB = xCxDxExF ^ S5[z7] ^ S6[z6] ^ S7[z5] ^ S8[z4] ^ S5[x9]; zCzDzEzF = x4x5x6x7 ^ S5[zA] ^ S6[z9] ^ S7[zB] ^ S8[z8] ^ S6[xB]; subkey[16] = (S5[z8] ^ S6[z9] ^ S7[z7] ^ S8[z6] ^ S5[z2]) & 0x1f; subkey[17] = (S5[zA] ^ S6[zB] ^ S7[z5] ^ S8[z4] ^ S6[z6]) & 0x1f; subkey[18] = (S5[zC] ^ S6[zD] ^ S7[z3] ^ S8[z2] ^ S7[z9]) & 0x1f; subkey[19] = (S5[zE] ^ S6[zF] ^ S7[z1] ^ S8[z0] ^ S8[zC]) & 0x1f; x0x1x2x3 = z8z9zAzB ^ S5[z5] ^ S6[z7] ^ S7[z4] ^ S8[z6] ^ S7[z0]; x4x5x6x7 = z0z1z2z3 ^ S5[x0] ^ S6[x2] ^ S7[x1] ^ S8[x3] ^ S8[z2]; x8x9xAxB = z4z5z6z7 ^ S5[x7] ^ S6[x6] ^ S7[x5] ^ S8[x4] ^ S5[z1]; xCxDxExF = zCzDzEzF ^ S5[xA] ^ S6[x9] ^ S7[xB] ^ S8[x8] ^ S6[z3]; subkey[20] = (S5[x3] ^ S6[x2] ^ S7[xC] ^ S8[xD] ^ S5[x8]) & 0x1f; subkey[21] = (S5[x1] ^ S6[x0] ^ S7[xE] ^ S8[xF] ^ S6[xD]) & 0x1f; subkey[22] = (S5[x7] ^ S6[x6] ^ S7[x8] ^ S8[x9] ^ S7[x3]) & 0x1f; subkey[23] = (S5[x5] ^ S6[x4] ^ S7[xA] ^ S8[xB] ^ S8[x7]) & 0x1f; z0z1z2z3 = x0x1x2x3 ^ S5[xD] ^ S6[xF] ^ S7[xC] ^ S8[xE] ^ S7[x8]; z4z5z6z7 = x8x9xAxB ^ S5[z0] ^ S6[z2] ^ S7[z1] ^ S8[z3] ^ S8[xA]; z8z9zAzB = xCxDxExF ^ S5[z7] ^ S6[z6] ^ S7[z5] ^ S8[z4] ^ S5[x9]; zCzDzEzF = x4x5x6x7 ^ S5[zA] ^ S6[z9] ^ S7[zB] ^ S8[z8] ^ S6[xB]; subkey[24] = (S5[z3] ^ S6[z2] ^ S7[zC] ^ S8[zD] ^ S5[z9]) & 0x1f; subkey[25] = (S5[z1] ^ S6[z0] ^ S7[zE] ^ S8[zF] ^ S6[zC]) & 0x1f; subkey[26] = (S5[z7] ^ S6[z6] ^ S7[z8] ^ S8[z9] ^ S7[z2]) & 0x1f; subkey[27] = (S5[z5] ^ S6[z4] ^ S7[zA] ^ S8[zB] ^ S8[z6]) & 0x1f; x0x1x2x3 = z8z9zAzB ^ S5[z5] ^ S6[z7] ^ S7[z4] ^ S8[z6] ^ S7[z0]; x4x5x6x7 = z0z1z2z3 ^ S5[x0] ^ S6[x2] ^ S7[x1] ^ S8[x3] ^ S8[z2]; x8x9xAxB = z4z5z6z7 ^ S5[x7] ^ S6[x6] ^ S7[x5] ^ S8[x4] ^ S5[z1]; xCxDxExF = zCzDzEzF ^ S5[xA] ^ S6[x9] ^ S7[xB] ^ S8[x8] ^ S6[z3]; subkey[28] = (S5[x8] ^ S6[x9] ^ S7[x7] ^ S8[x6] ^ S5[x3]) & 0x1f; subkey[29] = (S5[xA] ^ S6[xB] ^ S7[x5] ^ S8[x4] ^ S6[x7]) & 0x1f; subkey[30] = (S5[xC] ^ S6[xD] ^ S7[x3] ^ S8[x2] ^ S7[x8]) & 0x1f; subkey[31] = (S5[xE] ^ S6[xF] ^ S7[x1] ^ S8[x0] ^ S8[xD]) & 0x1f; } #define CAST128_TYPE1(rc, d, km, kr) { \ u_int32_t x = circular_leftshift(((km)+(d)), (kr)); \ (rc) = ((S1[byte0(x)] ^ S2[byte1(x)]) - S3[byte2(x)]) + S4[byte3(x)]; \ } #define CAST128_TYPE2(rc, d, km, kr) { \ u_int32_t x = circular_leftshift(((km)^(d)), (kr)); \ (rc) = ((S1[byte0(x)] - S2[byte1(x)]) + S3[byte2(x)]) ^ S4[byte3(x)]; \ } #define CAST128_TYPE3(rc, d, km, kr) { \ u_int32_t x = circular_leftshift(((km)-(d)), (kr)); \ (rc) = ((S1[byte0(x)] + S2[byte1(x)]) ^ S3[byte2(x)]) - S4[byte3(x)]; \ } void cast128_encrypt_round16(u_int8_t *c, const u_int8_t *m, u_int32_t *subkey) { u_int32_t l; /* left 32bit */ u_int32_t r; /* right 32bit */ u_int32_t br; /* backup right 32bit */ u_int32_t rc; /* result code of CAST128_TYPE?() */ u_int32_t *km, *kr; /* Step 2 */ l = (m[0] << 24) | (m[1] << 16) | (m[2] << 8) | m[3]; r = (m[4] << 24) | (m[5] << 16) | (m[6] << 8) | m[7]; /* Step 3 */ km = subkey; kr = subkey + 16; br = r; CAST128_TYPE1(rc, r, *km, *kr); r = l ^ rc; l = br; km++; kr++; br = r; CAST128_TYPE2(rc, r, *km, *kr); r = l ^ rc; l = br; km++; kr++; br = r; CAST128_TYPE3(rc, r, *km, *kr); r = l ^ rc; l = br; km++; kr++; br = r; CAST128_TYPE1(rc, r, *km, *kr); r = l ^ rc; l = br; km++; kr++; br = r; CAST128_TYPE2(rc, r, *km, *kr); r = l ^ rc; l = br; km++; kr++; br = r; CAST128_TYPE3(rc, r, *km, *kr); r = l ^ rc; l = br; km++; kr++; br = r; CAST128_TYPE1(rc, r, *km, *kr); r = l ^ rc; l = br; km++; kr++; br = r; CAST128_TYPE2(rc, r, *km, *kr); r = l ^ rc; l = br; km++; kr++; br = r; CAST128_TYPE3(rc, r, *km, *kr); r = l ^ rc; l = br; km++; kr++; br = r; CAST128_TYPE1(rc, r, *km, *kr); r = l ^ rc; l = br; km++; kr++; br = r; CAST128_TYPE2(rc, r, *km, *kr); r = l ^ rc; l = br; km++; kr++; br = r; CAST128_TYPE3(rc, r, *km, *kr); r = l ^ rc; l = br; km++; kr++; br = r; CAST128_TYPE1(rc, r, *km, *kr); r = l ^ rc; l = br; km++; kr++; br = r; CAST128_TYPE2(rc, r, *km, *kr); r = l ^ rc; l = br; km++; kr++; br = r; CAST128_TYPE3(rc, r, *km, *kr); r = l ^ rc; l = br; km++; kr++; br = r; CAST128_TYPE1(rc, r, *km, *kr); r = l ^ rc; l = br; /* Step 4 */ c[0] = (r >> 24) & 0xff; c[1] = (r >> 16) & 0xff; c[2] = (r >> 8) & 0xff; c[3] = r & 0xff; c[4] = (l >> 24) & 0xff; c[5] = (l >> 16) & 0xff; c[6] = (l >> 8) & 0xff; c[7] = l & 0xff; } void cast128_decrypt_round16(u_int8_t *m, const u_int8_t *c, u_int32_t *subkey) { u_int32_t l; /* left 32bit */ u_int32_t r; /* right 32bit */ u_int32_t bl; /* backup left 32bit */ u_int32_t rc; /* result code of CAST128_TYPE?() */ u_int32_t *km, *kr; /* Step 2 */ r = (c[0] << 24) | (c[1] << 16) | (c[2] << 8) | c[3]; l = (c[4] << 24) | (c[5] << 16) | (c[6] << 8) | c[7]; /* Step 3 */ km = subkey + 15; kr = subkey + 31; bl = l; CAST128_TYPE1(rc, l, *km, *kr); l = r ^ rc; r = bl; km--; kr--; bl = l; CAST128_TYPE3(rc, l, *km, *kr); l = r ^ rc; r = bl; km--; kr--; bl = l; CAST128_TYPE2(rc, l, *km, *kr); l = r ^ rc; r = bl; km--; kr--; bl = l; CAST128_TYPE1(rc, l, *km, *kr); l = r ^ rc; r = bl; km--; kr--; bl = l; CAST128_TYPE3(rc, l, *km, *kr); l = r ^ rc; r = bl; km--; kr--; bl = l; CAST128_TYPE2(rc, l, *km, *kr); l = r ^ rc; r = bl; km--; kr--; bl = l; CAST128_TYPE1(rc, l, *km, *kr); l = r ^ rc; r = bl; km--; kr--; bl = l; CAST128_TYPE3(rc, l, *km, *kr); l = r ^ rc; r = bl; km--; kr--; bl = l; CAST128_TYPE2(rc, l, *km, *kr); l = r ^ rc; r = bl; km--; kr--; bl = l; CAST128_TYPE1(rc, l, *km, *kr); l = r ^ rc; r = bl; km--; kr--; bl = l; CAST128_TYPE3(rc, l, *km, *kr); l = r ^ rc; r = bl; km--; kr--; bl = l; CAST128_TYPE2(rc, l, *km, *kr); l = r ^ rc; r = bl; km--; kr--; bl = l; CAST128_TYPE1(rc, l, *km, *kr); l = r ^ rc; r = bl; km--; kr--; bl = l; CAST128_TYPE3(rc, l, *km, *kr); l = r ^ rc; r = bl; km--; kr--; bl = l; CAST128_TYPE2(rc, l, *km, *kr); l = r ^ rc; r = bl; km--; kr--; bl = l; CAST128_TYPE1(rc, l, *km, *kr); l = r ^ rc; r = bl; /* Step 4 */ m[0] = (l >> 24) & 0xff; m[1] = (l >> 16) & 0xff; m[2] = (l >> 8) & 0xff; m[3] = l & 0xff; m[4] = (r >> 24) & 0xff; m[5] = (r >> 16) & 0xff; m[6] = (r >> 8) & 0xff; m[7] = r & 0xff; } void cast128_encrypt_round12(u_int8_t *c, const u_int8_t *m, u_int32_t *subkey) { u_int32_t l; /* left 32bit */ u_int32_t r; /* right 32bit */ u_int32_t br; /* backup right 32bit */ u_int32_t rc; /* result code of CAST128_TYPE?() */ u_int32_t *km, *kr; /* Step 2 */ l = (m[0] << 24) | (m[1] << 16) | (m[2] << 8) | m[3]; r = (m[4] << 24) | (m[5] << 16) | (m[6] << 8) | m[7]; /* Step 3 */ km = subkey; kr = subkey + 16; br = r; CAST128_TYPE1(rc, r, *km, *kr); r = l ^ rc; l = br; km++; kr++; br = r; CAST128_TYPE2(rc, r, *km, *kr); r = l ^ rc; l = br; km++; kr++; br = r; CAST128_TYPE3(rc, r, *km, *kr); r = l ^ rc; l = br; km++; kr++; br = r; CAST128_TYPE1(rc, r, *km, *kr); r = l ^ rc; l = br; km++; kr++; br = r; CAST128_TYPE2(rc, r, *km, *kr); r = l ^ rc; l = br; km++; kr++; br = r; CAST128_TYPE3(rc, r, *km, *kr); r = l ^ rc; l = br; km++; kr++; br = r; CAST128_TYPE1(rc, r, *km, *kr); r = l ^ rc; l = br; km++; kr++; br = r; CAST128_TYPE2(rc, r, *km, *kr); r = l ^ rc; l = br; km++; kr++; br = r; CAST128_TYPE3(rc, r, *km, *kr); r = l ^ rc; l = br; km++; kr++; br = r; CAST128_TYPE1(rc, r, *km, *kr); r = l ^ rc; l = br; km++; kr++; br = r; CAST128_TYPE2(rc, r, *km, *kr); r = l ^ rc; l = br; km++; kr++; br = r; CAST128_TYPE3(rc, r, *km, *kr); r = l ^ rc; l = br; /* Step 4 */ c[0] = (r >> 24) & 0xff; c[1] = (r >> 16) & 0xff; c[2] = (r >> 8) & 0xff; c[3] = r & 0xff; c[4] = (l >> 24) & 0xff; c[5] = (l >> 16) & 0xff; c[6] = (l >> 8) & 0xff; c[7] = l & 0xff; } void cast128_decrypt_round12(u_int8_t *m, const u_int8_t *c, u_int32_t *subkey) { u_int32_t l; /* left 32bit */ u_int32_t r; /* right 32bit */ u_int32_t bl; /* backup left 32bit */ u_int32_t rc; /* result code of CAST128_TYPE?() */ u_int32_t *km, *kr; /* Step 2 */ r = (c[0] << 24) | (c[1] << 16) | (c[2] << 8) | c[3]; l = (c[4] << 24) | (c[5] << 16) | (c[6] << 8) | c[7]; /* Step 3 */ km = subkey + 11; kr = subkey + 27; bl = l; CAST128_TYPE3(rc, l, *km, *kr); l = r ^ rc; r = bl; km--; kr--; bl = l; CAST128_TYPE2(rc, l, *km, *kr); l = r ^ rc; r = bl; km--; kr--; bl = l; CAST128_TYPE1(rc, l, *km, *kr); l = r ^ rc; r = bl; km--; kr--; bl = l; CAST128_TYPE3(rc, l, *km, *kr); l = r ^ rc; r = bl; km--; kr--; bl = l; CAST128_TYPE2(rc, l, *km, *kr); l = r ^ rc; r = bl; km--; kr--; bl = l; CAST128_TYPE1(rc, l, *km, *kr); l = r ^ rc; r = bl; km--; kr--; bl = l; CAST128_TYPE3(rc, l, *km, *kr); l = r ^ rc; r = bl; km--; kr--; bl = l; CAST128_TYPE2(rc, l, *km, *kr); l = r ^ rc; r = bl; km--; kr--; bl = l; CAST128_TYPE1(rc, l, *km, *kr); l = r ^ rc; r = bl; km--; kr--; bl = l; CAST128_TYPE3(rc, l, *km, *kr); l = r ^ rc; r = bl; km--; kr--; bl = l; CAST128_TYPE2(rc, l, *km, *kr); l = r ^ rc; r = bl; km--; kr--; bl = l; CAST128_TYPE1(rc, l, *km, *kr); l = r ^ rc; r = bl; /* Step 4 */ m[0] = (l >> 24) & 0xff; m[1] = (l >> 16) & 0xff; m[2] = (l >> 8) & 0xff; m[3] = l & 0xff; m[4] = (r >> 24) & 0xff; m[5] = (r >> 16) & 0xff; m[6] = (r >> 8) & 0xff; m[7] = r & 0xff; } static u_int32_t S1[] = { 0x30fb40d4, 0x9fa0ff0b, 0x6beccd2f, 0x3f258c7a, 0x1e213f2f, 0x9c004dd3, 0x6003e540, 0xcf9fc949, 0xbfd4af27, 0x88bbbdb5, 0xe2034090, 0x98d09675, 0x6e63a0e0, 0x15c361d2, 0xc2e7661d, 0x22d4ff8e, 0x28683b6f, 0xc07fd059, 0xff2379c8, 0x775f50e2, 0x43c340d3, 0xdf2f8656, 0x887ca41a, 0xa2d2bd2d, 0xa1c9e0d6, 0x346c4819, 0x61b76d87, 0x22540f2f, 0x2abe32e1, 0xaa54166b, 0x22568e3a, 0xa2d341d0, 0x66db40c8, 0xa784392f, 0x004dff2f, 0x2db9d2de, 0x97943fac, 0x4a97c1d8, 0x527644b7, 0xb5f437a7, 0xb82cbaef, 0xd751d159, 0x6ff7f0ed, 0x5a097a1f, 0x827b68d0, 0x90ecf52e, 0x22b0c054, 0xbc8e5935, 0x4b6d2f7f, 0x50bb64a2, 0xd2664910, 0xbee5812d, 0xb7332290, 0xe93b159f, 0xb48ee411, 0x4bff345d, 0xfd45c240, 0xad31973f, 0xc4f6d02e, 0x55fc8165, 0xd5b1caad, 0xa1ac2dae, 0xa2d4b76d, 0xc19b0c50, 0x882240f2, 0x0c6e4f38, 0xa4e4bfd7, 0x4f5ba272, 0x564c1d2f, 0xc59c5319, 0xb949e354, 0xb04669fe, 0xb1b6ab8a, 0xc71358dd, 0x6385c545, 0x110f935d, 0x57538ad5, 0x6a390493, 0xe63d37e0, 0x2a54f6b3, 0x3a787d5f, 0x6276a0b5, 0x19a6fcdf, 0x7a42206a, 0x29f9d4d5, 0xf61b1891, 0xbb72275e, 0xaa508167, 0x38901091, 0xc6b505eb, 0x84c7cb8c, 0x2ad75a0f, 0x874a1427, 0xa2d1936b, 0x2ad286af, 0xaa56d291, 0xd7894360, 0x425c750d, 0x93b39e26, 0x187184c9, 0x6c00b32d, 0x73e2bb14, 0xa0bebc3c, 0x54623779, 0x64459eab, 0x3f328b82, 0x7718cf82, 0x59a2cea6, 0x04ee002e, 0x89fe78e6, 0x3fab0950, 0x325ff6c2, 0x81383f05, 0x6963c5c8, 0x76cb5ad6, 0xd49974c9, 0xca180dcf, 0x380782d5, 0xc7fa5cf6, 0x8ac31511, 0x35e79e13, 0x47da91d0, 0xf40f9086, 0xa7e2419e, 0x31366241, 0x051ef495, 0xaa573b04, 0x4a805d8d, 0x548300d0, 0x00322a3c, 0xbf64cddf, 0xba57a68e, 0x75c6372b, 0x50afd341, 0xa7c13275, 0x915a0bf5, 0x6b54bfab, 0x2b0b1426, 0xab4cc9d7, 0x449ccd82, 0xf7fbf265, 0xab85c5f3, 0x1b55db94, 0xaad4e324, 0xcfa4bd3f, 0x2deaa3e2, 0x9e204d02, 0xc8bd25ac, 0xeadf55b3, 0xd5bd9e98, 0xe31231b2, 0x2ad5ad6c, 0x954329de, 0xadbe4528, 0xd8710f69, 0xaa51c90f, 0xaa786bf6, 0x22513f1e, 0xaa51a79b, 0x2ad344cc, 0x7b5a41f0, 0xd37cfbad, 0x1b069505, 0x41ece491, 0xb4c332e6, 0x032268d4, 0xc9600acc, 0xce387e6d, 0xbf6bb16c, 0x6a70fb78, 0x0d03d9c9, 0xd4df39de, 0xe01063da, 0x4736f464, 0x5ad328d8, 0xb347cc96, 0x75bb0fc3, 0x98511bfb, 0x4ffbcc35, 0xb58bcf6a, 0xe11f0abc, 0xbfc5fe4a, 0xa70aec10, 0xac39570a, 0x3f04442f, 0x6188b153, 0xe0397a2e, 0x5727cb79, 0x9ceb418f, 0x1cacd68d, 0x2ad37c96, 0x0175cb9d, 0xc69dff09, 0xc75b65f0, 0xd9db40d8, 0xec0e7779, 0x4744ead4, 0xb11c3274, 0xdd24cb9e, 0x7e1c54bd, 0xf01144f9, 0xd2240eb1, 0x9675b3fd, 0xa3ac3755, 0xd47c27af, 0x51c85f4d, 0x56907596, 0xa5bb15e6, 0x580304f0, 0xca042cf1, 0x011a37ea, 0x8dbfaadb, 0x35ba3e4a, 0x3526ffa0, 0xc37b4d09, 0xbc306ed9, 0x98a52666, 0x5648f725, 0xff5e569d, 0x0ced63d0, 0x7c63b2cf, 0x700b45e1, 0xd5ea50f1, 0x85a92872, 0xaf1fbda7, 0xd4234870, 0xa7870bf3, 0x2d3b4d79, 0x42e04198, 0x0cd0ede7, 0x26470db8, 0xf881814c, 0x474d6ad7, 0x7c0c5e5c, 0xd1231959, 0x381b7298, 0xf5d2f4db, 0xab838653, 0x6e2f1e23, 0x83719c9e, 0xbd91e046, 0x9a56456e, 0xdc39200c, 0x20c8c571, 0x962bda1c, 0xe1e696ff, 0xb141ab08, 0x7cca89b9, 0x1a69e783, 0x02cc4843, 0xa2f7c579, 0x429ef47d, 0x427b169c, 0x5ac9f049, 0xdd8f0f00, 0x5c8165bf, }; static u_int32_t S2[] = { 0x1f201094, 0xef0ba75b, 0x69e3cf7e, 0x393f4380, 0xfe61cf7a, 0xeec5207a, 0x55889c94, 0x72fc0651, 0xada7ef79, 0x4e1d7235, 0xd55a63ce, 0xde0436ba, 0x99c430ef, 0x5f0c0794, 0x18dcdb7d, 0xa1d6eff3, 0xa0b52f7b, 0x59e83605, 0xee15b094, 0xe9ffd909, 0xdc440086, 0xef944459, 0xba83ccb3, 0xe0c3cdfb, 0xd1da4181, 0x3b092ab1, 0xf997f1c1, 0xa5e6cf7b, 0x01420ddb, 0xe4e7ef5b, 0x25a1ff41, 0xe180f806, 0x1fc41080, 0x179bee7a, 0xd37ac6a9, 0xfe5830a4, 0x98de8b7f, 0x77e83f4e, 0x79929269, 0x24fa9f7b, 0xe113c85b, 0xacc40083, 0xd7503525, 0xf7ea615f, 0x62143154, 0x0d554b63, 0x5d681121, 0xc866c359, 0x3d63cf73, 0xcee234c0, 0xd4d87e87, 0x5c672b21, 0x071f6181, 0x39f7627f, 0x361e3084, 0xe4eb573b, 0x602f64a4, 0xd63acd9c, 0x1bbc4635, 0x9e81032d, 0x2701f50c, 0x99847ab4, 0xa0e3df79, 0xba6cf38c, 0x10843094, 0x2537a95e, 0xf46f6ffe, 0xa1ff3b1f, 0x208cfb6a, 0x8f458c74, 0xd9e0a227, 0x4ec73a34, 0xfc884f69, 0x3e4de8df, 0xef0e0088, 0x3559648d, 0x8a45388c, 0x1d804366, 0x721d9bfd, 0xa58684bb, 0xe8256333, 0x844e8212, 0x128d8098, 0xfed33fb4, 0xce280ae1, 0x27e19ba5, 0xd5a6c252, 0xe49754bd, 0xc5d655dd, 0xeb667064, 0x77840b4d, 0xa1b6a801, 0x84db26a9, 0xe0b56714, 0x21f043b7, 0xe5d05860, 0x54f03084, 0x066ff472, 0xa31aa153, 0xdadc4755, 0xb5625dbf, 0x68561be6, 0x83ca6b94, 0x2d6ed23b, 0xeccf01db, 0xa6d3d0ba, 0xb6803d5c, 0xaf77a709, 0x33b4a34c, 0x397bc8d6, 0x5ee22b95, 0x5f0e5304, 0x81ed6f61, 0x20e74364, 0xb45e1378, 0xde18639b, 0x881ca122, 0xb96726d1, 0x8049a7e8, 0x22b7da7b, 0x5e552d25, 0x5272d237, 0x79d2951c, 0xc60d894c, 0x488cb402, 0x1ba4fe5b, 0xa4b09f6b, 0x1ca815cf, 0xa20c3005, 0x8871df63, 0xb9de2fcb, 0x0cc6c9e9, 0x0beeff53, 0xe3214517, 0xb4542835, 0x9f63293c, 0xee41e729, 0x6e1d2d7c, 0x50045286, 0x1e6685f3, 0xf33401c6, 0x30a22c95, 0x31a70850, 0x60930f13, 0x73f98417, 0xa1269859, 0xec645c44, 0x52c877a9, 0xcdff33a6, 0xa02b1741, 0x7cbad9a2, 0x2180036f, 0x50d99c08, 0xcb3f4861, 0xc26bd765, 0x64a3f6ab, 0x80342676, 0x25a75e7b, 0xe4e6d1fc, 0x20c710e6, 0xcdf0b680, 0x17844d3b, 0x31eef84d, 0x7e0824e4, 0x2ccb49eb, 0x846a3bae, 0x8ff77888, 0xee5d60f6, 0x7af75673, 0x2fdd5cdb, 0xa11631c1, 0x30f66f43, 0xb3faec54, 0x157fd7fa, 0xef8579cc, 0xd152de58, 0xdb2ffd5e, 0x8f32ce19, 0x306af97a, 0x02f03ef8, 0x99319ad5, 0xc242fa0f, 0xa7e3ebb0, 0xc68e4906, 0xb8da230c, 0x80823028, 0xdcdef3c8, 0xd35fb171, 0x088a1bc8, 0xbec0c560, 0x61a3c9e8, 0xbca8f54d, 0xc72feffa, 0x22822e99, 0x82c570b4, 0xd8d94e89, 0x8b1c34bc, 0x301e16e6, 0x273be979, 0xb0ffeaa6, 0x61d9b8c6, 0x00b24869, 0xb7ffce3f, 0x08dc283b, 0x43daf65a, 0xf7e19798, 0x7619b72f, 0x8f1c9ba4, 0xdc8637a0, 0x16a7d3b1, 0x9fc393b7, 0xa7136eeb, 0xc6bcc63e, 0x1a513742, 0xef6828bc, 0x520365d6, 0x2d6a77ab, 0x3527ed4b, 0x821fd216, 0x095c6e2e, 0xdb92f2fb, 0x5eea29cb, 0x145892f5, 0x91584f7f, 0x5483697b, 0x2667a8cc, 0x85196048, 0x8c4bacea, 0x833860d4, 0x0d23e0f9, 0x6c387e8a, 0x0ae6d249, 0xb284600c, 0xd835731d, 0xdcb1c647, 0xac4c56ea, 0x3ebd81b3, 0x230eabb0, 0x6438bc87, 0xf0b5b1fa, 0x8f5ea2b3, 0xfc184642, 0x0a036b7a, 0x4fb089bd, 0x649da589, 0xa345415e, 0x5c038323, 0x3e5d3bb9, 0x43d79572, 0x7e6dd07c, 0x06dfdf1e, 0x6c6cc4ef, 0x7160a539, 0x73bfbe70, 0x83877605, 0x4523ecf1, }; static u_int32_t S3[] = { 0x8defc240, 0x25fa5d9f, 0xeb903dbf, 0xe810c907, 0x47607fff, 0x369fe44b, 0x8c1fc644, 0xaececa90, 0xbeb1f9bf, 0xeefbcaea, 0xe8cf1950, 0x51df07ae, 0x920e8806, 0xf0ad0548, 0xe13c8d83, 0x927010d5, 0x11107d9f, 0x07647db9, 0xb2e3e4d4, 0x3d4f285e, 0xb9afa820, 0xfade82e0, 0xa067268b, 0x8272792e, 0x553fb2c0, 0x489ae22b, 0xd4ef9794, 0x125e3fbc, 0x21fffcee, 0x825b1bfd, 0x9255c5ed, 0x1257a240, 0x4e1a8302, 0xbae07fff, 0x528246e7, 0x8e57140e, 0x3373f7bf, 0x8c9f8188, 0xa6fc4ee8, 0xc982b5a5, 0xa8c01db7, 0x579fc264, 0x67094f31, 0xf2bd3f5f, 0x40fff7c1, 0x1fb78dfc, 0x8e6bd2c1, 0x437be59b, 0x99b03dbf, 0xb5dbc64b, 0x638dc0e6, 0x55819d99, 0xa197c81c, 0x4a012d6e, 0xc5884a28, 0xccc36f71, 0xb843c213, 0x6c0743f1, 0x8309893c, 0x0feddd5f, 0x2f7fe850, 0xd7c07f7e, 0x02507fbf, 0x5afb9a04, 0xa747d2d0, 0x1651192e, 0xaf70bf3e, 0x58c31380, 0x5f98302e, 0x727cc3c4, 0x0a0fb402, 0x0f7fef82, 0x8c96fdad, 0x5d2c2aae, 0x8ee99a49, 0x50da88b8, 0x8427f4a0, 0x1eac5790, 0x796fb449, 0x8252dc15, 0xefbd7d9b, 0xa672597d, 0xada840d8, 0x45f54504, 0xfa5d7403, 0xe83ec305, 0x4f91751a, 0x925669c2, 0x23efe941, 0xa903f12e, 0x60270df2, 0x0276e4b6, 0x94fd6574, 0x927985b2, 0x8276dbcb, 0x02778176, 0xf8af918d, 0x4e48f79e, 0x8f616ddf, 0xe29d840e, 0x842f7d83, 0x340ce5c8, 0x96bbb682, 0x93b4b148, 0xef303cab, 0x984faf28, 0x779faf9b, 0x92dc560d, 0x224d1e20, 0x8437aa88, 0x7d29dc96, 0x2756d3dc, 0x8b907cee, 0xb51fd240, 0xe7c07ce3, 0xe566b4a1, 0xc3e9615e, 0x3cf8209d, 0x6094d1e3, 0xcd9ca341, 0x5c76460e, 0x00ea983b, 0xd4d67881, 0xfd47572c, 0xf76cedd9, 0xbda8229c, 0x127dadaa, 0x438a074e, 0x1f97c090, 0x081bdb8a, 0x93a07ebe, 0xb938ca15, 0x97b03cff, 0x3dc2c0f8, 0x8d1ab2ec, 0x64380e51, 0x68cc7bfb, 0xd90f2788, 0x12490181, 0x5de5ffd4, 0xdd7ef86a, 0x76a2e214, 0xb9a40368, 0x925d958f, 0x4b39fffa, 0xba39aee9, 0xa4ffd30b, 0xfaf7933b, 0x6d498623, 0x193cbcfa, 0x27627545, 0x825cf47a, 0x61bd8ba0, 0xd11e42d1, 0xcead04f4, 0x127ea392, 0x10428db7, 0x8272a972, 0x9270c4a8, 0x127de50b, 0x285ba1c8, 0x3c62f44f, 0x35c0eaa5, 0xe805d231, 0x428929fb, 0xb4fcdf82, 0x4fb66a53, 0x0e7dc15b, 0x1f081fab, 0x108618ae, 0xfcfd086d, 0xf9ff2889, 0x694bcc11, 0x236a5cae, 0x12deca4d, 0x2c3f8cc5, 0xd2d02dfe, 0xf8ef5896, 0xe4cf52da, 0x95155b67, 0x494a488c, 0xb9b6a80c, 0x5c8f82bc, 0x89d36b45, 0x3a609437, 0xec00c9a9, 0x44715253, 0x0a874b49, 0xd773bc40, 0x7c34671c, 0x02717ef6, 0x4feb5536, 0xa2d02fff, 0xd2bf60c4, 0xd43f03c0, 0x50b4ef6d, 0x07478cd1, 0x006e1888, 0xa2e53f55, 0xb9e6d4bc, 0xa2048016, 0x97573833, 0xd7207d67, 0xde0f8f3d, 0x72f87b33, 0xabcc4f33, 0x7688c55d, 0x7b00a6b0, 0x947b0001, 0x570075d2, 0xf9bb88f8, 0x8942019e, 0x4264a5ff, 0x856302e0, 0x72dbd92b, 0xee971b69, 0x6ea22fde, 0x5f08ae2b, 0xaf7a616d, 0xe5c98767, 0xcf1febd2, 0x61efc8c2, 0xf1ac2571, 0xcc8239c2, 0x67214cb8, 0xb1e583d1, 0xb7dc3e62, 0x7f10bdce, 0xf90a5c38, 0x0ff0443d, 0x606e6dc6, 0x60543a49, 0x5727c148, 0x2be98a1d, 0x8ab41738, 0x20e1be24, 0xaf96da0f, 0x68458425, 0x99833be5, 0x600d457d, 0x282f9350, 0x8334b362, 0xd91d1120, 0x2b6d8da0, 0x642b1e31, 0x9c305a00, 0x52bce688, 0x1b03588a, 0xf7baefd5, 0x4142ed9c, 0xa4315c11, 0x83323ec5, 0xdfef4636, 0xa133c501, 0xe9d3531c, 0xee353783, }; static u_int32_t S4[] = { 0x9db30420, 0x1fb6e9de, 0xa7be7bef, 0xd273a298, 0x4a4f7bdb, 0x64ad8c57, 0x85510443, 0xfa020ed1, 0x7e287aff, 0xe60fb663, 0x095f35a1, 0x79ebf120, 0xfd059d43, 0x6497b7b1, 0xf3641f63, 0x241e4adf, 0x28147f5f, 0x4fa2b8cd, 0xc9430040, 0x0cc32220, 0xfdd30b30, 0xc0a5374f, 0x1d2d00d9, 0x24147b15, 0xee4d111a, 0x0fca5167, 0x71ff904c, 0x2d195ffe, 0x1a05645f, 0x0c13fefe, 0x081b08ca, 0x05170121, 0x80530100, 0xe83e5efe, 0xac9af4f8, 0x7fe72701, 0xd2b8ee5f, 0x06df4261, 0xbb9e9b8a, 0x7293ea25, 0xce84ffdf, 0xf5718801, 0x3dd64b04, 0xa26f263b, 0x7ed48400, 0x547eebe6, 0x446d4ca0, 0x6cf3d6f5, 0x2649abdf, 0xaea0c7f5, 0x36338cc1, 0x503f7e93, 0xd3772061, 0x11b638e1, 0x72500e03, 0xf80eb2bb, 0xabe0502e, 0xec8d77de, 0x57971e81, 0xe14f6746, 0xc9335400, 0x6920318f, 0x081dbb99, 0xffc304a5, 0x4d351805, 0x7f3d5ce3, 0xa6c866c6, 0x5d5bcca9, 0xdaec6fea, 0x9f926f91, 0x9f46222f, 0x3991467d, 0xa5bf6d8e, 0x1143c44f, 0x43958302, 0xd0214eeb, 0x022083b8, 0x3fb6180c, 0x18f8931e, 0x281658e6, 0x26486e3e, 0x8bd78a70, 0x7477e4c1, 0xb506e07c, 0xf32d0a25, 0x79098b02, 0xe4eabb81, 0x28123b23, 0x69dead38, 0x1574ca16, 0xdf871b62, 0x211c40b7, 0xa51a9ef9, 0x0014377b, 0x041e8ac8, 0x09114003, 0xbd59e4d2, 0xe3d156d5, 0x4fe876d5, 0x2f91a340, 0x557be8de, 0x00eae4a7, 0x0ce5c2ec, 0x4db4bba6, 0xe756bdff, 0xdd3369ac, 0xec17b035, 0x06572327, 0x99afc8b0, 0x56c8c391, 0x6b65811c, 0x5e146119, 0x6e85cb75, 0xbe07c002, 0xc2325577, 0x893ff4ec, 0x5bbfc92d, 0xd0ec3b25, 0xb7801ab7, 0x8d6d3b24, 0x20c763ef, 0xc366a5fc, 0x9c382880, 0x0ace3205, 0xaac9548a, 0xeca1d7c7, 0x041afa32, 0x1d16625a, 0x6701902c, 0x9b757a54, 0x31d477f7, 0x9126b031, 0x36cc6fdb, 0xc70b8b46, 0xd9e66a48, 0x56e55a79, 0x026a4ceb, 0x52437eff, 0x2f8f76b4, 0x0df980a5, 0x8674cde3, 0xedda04eb, 0x17a9be04, 0x2c18f4df, 0xb7747f9d, 0xab2af7b4, 0xefc34d20, 0x2e096b7c, 0x1741a254, 0xe5b6a035, 0x213d42f6, 0x2c1c7c26, 0x61c2f50f, 0x6552daf9, 0xd2c231f8, 0x25130f69, 0xd8167fa2, 0x0418f2c8, 0x001a96a6, 0x0d1526ab, 0x63315c21, 0x5e0a72ec, 0x49bafefd, 0x187908d9, 0x8d0dbd86, 0x311170a7, 0x3e9b640c, 0xcc3e10d7, 0xd5cad3b6, 0x0caec388, 0xf73001e1, 0x6c728aff, 0x71eae2a1, 0x1f9af36e, 0xcfcbd12f, 0xc1de8417, 0xac07be6b, 0xcb44a1d8, 0x8b9b0f56, 0x013988c3, 0xb1c52fca, 0xb4be31cd, 0xd8782806, 0x12a3a4e2, 0x6f7de532, 0x58fd7eb6, 0xd01ee900, 0x24adffc2, 0xf4990fc5, 0x9711aac5, 0x001d7b95, 0x82e5e7d2, 0x109873f6, 0x00613096, 0xc32d9521, 0xada121ff, 0x29908415, 0x7fbb977f, 0xaf9eb3db, 0x29c9ed2a, 0x5ce2a465, 0xa730f32c, 0xd0aa3fe8, 0x8a5cc091, 0xd49e2ce7, 0x0ce454a9, 0xd60acd86, 0x015f1919, 0x77079103, 0xdea03af6, 0x78a8565e, 0xdee356df, 0x21f05cbe, 0x8b75e387, 0xb3c50651, 0xb8a5c3ef, 0xd8eeb6d2, 0xe523be77, 0xc2154529, 0x2f69efdf, 0xafe67afb, 0xf470c4b2, 0xf3e0eb5b, 0xd6cc9876, 0x39e4460c, 0x1fda8538, 0x1987832f, 0xca007367, 0xa99144f8, 0x296b299e, 0x492fc295, 0x9266beab, 0xb5676e69, 0x9bd3ddda, 0xdf7e052f, 0xdb25701c, 0x1b5e51ee, 0xf65324e6, 0x6afce36c, 0x0316cc04, 0x8644213e, 0xb7dc59d0, 0x7965291f, 0xccd6fd43, 0x41823979, 0x932bcdf6, 0xb657c34d, 0x4edfd282, 0x7ae5290c, 0x3cb9536b, 0x851e20fe, 0x9833557e, 0x13ecf0b0, 0xd3ffb372, 0x3f85c5c1, 0x0aef7ed2, }; static u_int32_t S5[] = { 0x7ec90c04, 0x2c6e74b9, 0x9b0e66df, 0xa6337911, 0xb86a7fff, 0x1dd358f5, 0x44dd9d44, 0x1731167f, 0x08fbf1fa, 0xe7f511cc, 0xd2051b00, 0x735aba00, 0x2ab722d8, 0x386381cb, 0xacf6243a, 0x69befd7a, 0xe6a2e77f, 0xf0c720cd, 0xc4494816, 0xccf5c180, 0x38851640, 0x15b0a848, 0xe68b18cb, 0x4caadeff, 0x5f480a01, 0x0412b2aa, 0x259814fc, 0x41d0efe2, 0x4e40b48d, 0x248eb6fb, 0x8dba1cfe, 0x41a99b02, 0x1a550a04, 0xba8f65cb, 0x7251f4e7, 0x95a51725, 0xc106ecd7, 0x97a5980a, 0xc539b9aa, 0x4d79fe6a, 0xf2f3f763, 0x68af8040, 0xed0c9e56, 0x11b4958b, 0xe1eb5a88, 0x8709e6b0, 0xd7e07156, 0x4e29fea7, 0x6366e52d, 0x02d1c000, 0xc4ac8e05, 0x9377f571, 0x0c05372a, 0x578535f2, 0x2261be02, 0xd642a0c9, 0xdf13a280, 0x74b55bd2, 0x682199c0, 0xd421e5ec, 0x53fb3ce8, 0xc8adedb3, 0x28a87fc9, 0x3d959981, 0x5c1ff900, 0xfe38d399, 0x0c4eff0b, 0x062407ea, 0xaa2f4fb1, 0x4fb96976, 0x90c79505, 0xb0a8a774, 0xef55a1ff, 0xe59ca2c2, 0xa6b62d27, 0xe66a4263, 0xdf65001f, 0x0ec50966, 0xdfdd55bc, 0x29de0655, 0x911e739a, 0x17af8975, 0x32c7911c, 0x89f89468, 0x0d01e980, 0x524755f4, 0x03b63cc9, 0x0cc844b2, 0xbcf3f0aa, 0x87ac36e9, 0xe53a7426, 0x01b3d82b, 0x1a9e7449, 0x64ee2d7e, 0xcddbb1da, 0x01c94910, 0xb868bf80, 0x0d26f3fd, 0x9342ede7, 0x04a5c284, 0x636737b6, 0x50f5b616, 0xf24766e3, 0x8eca36c1, 0x136e05db, 0xfef18391, 0xfb887a37, 0xd6e7f7d4, 0xc7fb7dc9, 0x3063fcdf, 0xb6f589de, 0xec2941da, 0x26e46695, 0xb7566419, 0xf654efc5, 0xd08d58b7, 0x48925401, 0xc1bacb7f, 0xe5ff550f, 0xb6083049, 0x5bb5d0e8, 0x87d72e5a, 0xab6a6ee1, 0x223a66ce, 0xc62bf3cd, 0x9e0885f9, 0x68cb3e47, 0x086c010f, 0xa21de820, 0xd18b69de, 0xf3f65777, 0xfa02c3f6, 0x407edac3, 0xcbb3d550, 0x1793084d, 0xb0d70eba, 0x0ab378d5, 0xd951fb0c, 0xded7da56, 0x4124bbe4, 0x94ca0b56, 0x0f5755d1, 0xe0e1e56e, 0x6184b5be, 0x580a249f, 0x94f74bc0, 0xe327888e, 0x9f7b5561, 0xc3dc0280, 0x05687715, 0x646c6bd7, 0x44904db3, 0x66b4f0a3, 0xc0f1648a, 0x697ed5af, 0x49e92ff6, 0x309e374f, 0x2cb6356a, 0x85808573, 0x4991f840, 0x76f0ae02, 0x083be84d, 0x28421c9a, 0x44489406, 0x736e4cb8, 0xc1092910, 0x8bc95fc6, 0x7d869cf4, 0x134f616f, 0x2e77118d, 0xb31b2be1, 0xaa90b472, 0x3ca5d717, 0x7d161bba, 0x9cad9010, 0xaf462ba2, 0x9fe459d2, 0x45d34559, 0xd9f2da13, 0xdbc65487, 0xf3e4f94e, 0x176d486f, 0x097c13ea, 0x631da5c7, 0x445f7382, 0x175683f4, 0xcdc66a97, 0x70be0288, 0xb3cdcf72, 0x6e5dd2f3, 0x20936079, 0x459b80a5, 0xbe60e2db, 0xa9c23101, 0xeba5315c, 0x224e42f2, 0x1c5c1572, 0xf6721b2c, 0x1ad2fff3, 0x8c25404e, 0x324ed72f, 0x4067b7fd, 0x0523138e, 0x5ca3bc78, 0xdc0fd66e, 0x75922283, 0x784d6b17, 0x58ebb16e, 0x44094f85, 0x3f481d87, 0xfcfeae7b, 0x77b5ff76, 0x8c2302bf, 0xaaf47556, 0x5f46b02a, 0x2b092801, 0x3d38f5f7, 0x0ca81f36, 0x52af4a8a, 0x66d5e7c0, 0xdf3b0874, 0x95055110, 0x1b5ad7a8, 0xf61ed5ad, 0x6cf6e479, 0x20758184, 0xd0cefa65, 0x88f7be58, 0x4a046826, 0x0ff6f8f3, 0xa09c7f70, 0x5346aba0, 0x5ce96c28, 0xe176eda3, 0x6bac307f, 0x376829d2, 0x85360fa9, 0x17e3fe2a, 0x24b79767, 0xf5a96b20, 0xd6cd2595, 0x68ff1ebf, 0x7555442c, 0xf19f06be, 0xf9e0659a, 0xeeb9491d, 0x34010718, 0xbb30cab8, 0xe822fe15, 0x88570983, 0x750e6249, 0xda627e55, 0x5e76ffa8, 0xb1534546, 0x6d47de08, 0xefe9e7d4, }; static u_int32_t S6[] = { 0xf6fa8f9d, 0x2cac6ce1, 0x4ca34867, 0xe2337f7c, 0x95db08e7, 0x016843b4, 0xeced5cbc, 0x325553ac, 0xbf9f0960, 0xdfa1e2ed, 0x83f0579d, 0x63ed86b9, 0x1ab6a6b8, 0xde5ebe39, 0xf38ff732, 0x8989b138, 0x33f14961, 0xc01937bd, 0xf506c6da, 0xe4625e7e, 0xa308ea99, 0x4e23e33c, 0x79cbd7cc, 0x48a14367, 0xa3149619, 0xfec94bd5, 0xa114174a, 0xeaa01866, 0xa084db2d, 0x09a8486f, 0xa888614a, 0x2900af98, 0x01665991, 0xe1992863, 0xc8f30c60, 0x2e78ef3c, 0xd0d51932, 0xcf0fec14, 0xf7ca07d2, 0xd0a82072, 0xfd41197e, 0x9305a6b0, 0xe86be3da, 0x74bed3cd, 0x372da53c, 0x4c7f4448, 0xdab5d440, 0x6dba0ec3, 0x083919a7, 0x9fbaeed9, 0x49dbcfb0, 0x4e670c53, 0x5c3d9c01, 0x64bdb941, 0x2c0e636a, 0xba7dd9cd, 0xea6f7388, 0xe70bc762, 0x35f29adb, 0x5c4cdd8d, 0xf0d48d8c, 0xb88153e2, 0x08a19866, 0x1ae2eac8, 0x284caf89, 0xaa928223, 0x9334be53, 0x3b3a21bf, 0x16434be3, 0x9aea3906, 0xefe8c36e, 0xf890cdd9, 0x80226dae, 0xc340a4a3, 0xdf7e9c09, 0xa694a807, 0x5b7c5ecc, 0x221db3a6, 0x9a69a02f, 0x68818a54, 0xceb2296f, 0x53c0843a, 0xfe893655, 0x25bfe68a, 0xb4628abc, 0xcf222ebf, 0x25ac6f48, 0xa9a99387, 0x53bddb65, 0xe76ffbe7, 0xe967fd78, 0x0ba93563, 0x8e342bc1, 0xe8a11be9, 0x4980740d, 0xc8087dfc, 0x8de4bf99, 0xa11101a0, 0x7fd37975, 0xda5a26c0, 0xe81f994f, 0x9528cd89, 0xfd339fed, 0xb87834bf, 0x5f04456d, 0x22258698, 0xc9c4c83b, 0x2dc156be, 0x4f628daa, 0x57f55ec5, 0xe2220abe, 0xd2916ebf, 0x4ec75b95, 0x24f2c3c0, 0x42d15d99, 0xcd0d7fa0, 0x7b6e27ff, 0xa8dc8af0, 0x7345c106, 0xf41e232f, 0x35162386, 0xe6ea8926, 0x3333b094, 0x157ec6f2, 0x372b74af, 0x692573e4, 0xe9a9d848, 0xf3160289, 0x3a62ef1d, 0xa787e238, 0xf3a5f676, 0x74364853, 0x20951063, 0x4576698d, 0xb6fad407, 0x592af950, 0x36f73523, 0x4cfb6e87, 0x7da4cec0, 0x6c152daa, 0xcb0396a8, 0xc50dfe5d, 0xfcd707ab, 0x0921c42f, 0x89dff0bb, 0x5fe2be78, 0x448f4f33, 0x754613c9, 0x2b05d08d, 0x48b9d585, 0xdc049441, 0xc8098f9b, 0x7dede786, 0xc39a3373, 0x42410005, 0x6a091751, 0x0ef3c8a6, 0x890072d6, 0x28207682, 0xa9a9f7be, 0xbf32679d, 0xd45b5b75, 0xb353fd00, 0xcbb0e358, 0x830f220a, 0x1f8fb214, 0xd372cf08, 0xcc3c4a13, 0x8cf63166, 0x061c87be, 0x88c98f88, 0x6062e397, 0x47cf8e7a, 0xb6c85283, 0x3cc2acfb, 0x3fc06976, 0x4e8f0252, 0x64d8314d, 0xda3870e3, 0x1e665459, 0xc10908f0, 0x513021a5, 0x6c5b68b7, 0x822f8aa0, 0x3007cd3e, 0x74719eef, 0xdc872681, 0x073340d4, 0x7e432fd9, 0x0c5ec241, 0x8809286c, 0xf592d891, 0x08a930f6, 0x957ef305, 0xb7fbffbd, 0xc266e96f, 0x6fe4ac98, 0xb173ecc0, 0xbc60b42a, 0x953498da, 0xfba1ae12, 0x2d4bd736, 0x0f25faab, 0xa4f3fceb, 0xe2969123, 0x257f0c3d, 0x9348af49, 0x361400bc, 0xe8816f4a, 0x3814f200, 0xa3f94043, 0x9c7a54c2, 0xbc704f57, 0xda41e7f9, 0xc25ad33a, 0x54f4a084, 0xb17f5505, 0x59357cbe, 0xedbd15c8, 0x7f97c5ab, 0xba5ac7b5, 0xb6f6deaf, 0x3a479c3a, 0x5302da25, 0x653d7e6a, 0x54268d49, 0x51a477ea, 0x5017d55b, 0xd7d25d88, 0x44136c76, 0x0404a8c8, 0xb8e5a121, 0xb81a928a, 0x60ed5869, 0x97c55b96, 0xeaec991b, 0x29935913, 0x01fdb7f1, 0x088e8dfa, 0x9ab6f6f5, 0x3b4cbf9f, 0x4a5de3ab, 0xe6051d35, 0xa0e1d855, 0xd36b4cf1, 0xf544edeb, 0xb0e93524, 0xbebb8fbd, 0xa2d762cf, 0x49c92f54, 0x38b5f331, 0x7128a454, 0x48392905, 0xa65b1db8, 0x851c97bd, 0xd675cf2f, }; static u_int32_t S7[] = { 0x85e04019, 0x332bf567, 0x662dbfff, 0xcfc65693, 0x2a8d7f6f, 0xab9bc912, 0xde6008a1, 0x2028da1f, 0x0227bce7, 0x4d642916, 0x18fac300, 0x50f18b82, 0x2cb2cb11, 0xb232e75c, 0x4b3695f2, 0xb28707de, 0xa05fbcf6, 0xcd4181e9, 0xe150210c, 0xe24ef1bd, 0xb168c381, 0xfde4e789, 0x5c79b0d8, 0x1e8bfd43, 0x4d495001, 0x38be4341, 0x913cee1d, 0x92a79c3f, 0x089766be, 0xbaeeadf4, 0x1286becf, 0xb6eacb19, 0x2660c200, 0x7565bde4, 0x64241f7a, 0x8248dca9, 0xc3b3ad66, 0x28136086, 0x0bd8dfa8, 0x356d1cf2, 0x107789be, 0xb3b2e9ce, 0x0502aa8f, 0x0bc0351e, 0x166bf52a, 0xeb12ff82, 0xe3486911, 0xd34d7516, 0x4e7b3aff, 0x5f43671b, 0x9cf6e037, 0x4981ac83, 0x334266ce, 0x8c9341b7, 0xd0d854c0, 0xcb3a6c88, 0x47bc2829, 0x4725ba37, 0xa66ad22b, 0x7ad61f1e, 0x0c5cbafa, 0x4437f107, 0xb6e79962, 0x42d2d816, 0x0a961288, 0xe1a5c06e, 0x13749e67, 0x72fc081a, 0xb1d139f7, 0xf9583745, 0xcf19df58, 0xbec3f756, 0xc06eba30, 0x07211b24, 0x45c28829, 0xc95e317f, 0xbc8ec511, 0x38bc46e9, 0xc6e6fa14, 0xbae8584a, 0xad4ebc46, 0x468f508b, 0x7829435f, 0xf124183b, 0x821dba9f, 0xaff60ff4, 0xea2c4e6d, 0x16e39264, 0x92544a8b, 0x009b4fc3, 0xaba68ced, 0x9ac96f78, 0x06a5b79a, 0xb2856e6e, 0x1aec3ca9, 0xbe838688, 0x0e0804e9, 0x55f1be56, 0xe7e5363b, 0xb3a1f25d, 0xf7debb85, 0x61fe033c, 0x16746233, 0x3c034c28, 0xda6d0c74, 0x79aac56c, 0x3ce4e1ad, 0x51f0c802, 0x98f8f35a, 0x1626a49f, 0xeed82b29, 0x1d382fe3, 0x0c4fb99a, 0xbb325778, 0x3ec6d97b, 0x6e77a6a9, 0xcb658b5c, 0xd45230c7, 0x2bd1408b, 0x60c03eb7, 0xb9068d78, 0xa33754f4, 0xf430c87d, 0xc8a71302, 0xb96d8c32, 0xebd4e7be, 0xbe8b9d2d, 0x7979fb06, 0xe7225308, 0x8b75cf77, 0x11ef8da4, 0xe083c858, 0x8d6b786f, 0x5a6317a6, 0xfa5cf7a0, 0x5dda0033, 0xf28ebfb0, 0xf5b9c310, 0xa0eac280, 0x08b9767a, 0xa3d9d2b0, 0x79d34217, 0x021a718d, 0x9ac6336a, 0x2711fd60, 0x438050e3, 0x069908a8, 0x3d7fedc4, 0x826d2bef, 0x4eeb8476, 0x488dcf25, 0x36c9d566, 0x28e74e41, 0xc2610aca, 0x3d49a9cf, 0xbae3b9df, 0xb65f8de6, 0x92aeaf64, 0x3ac7d5e6, 0x9ea80509, 0xf22b017d, 0xa4173f70, 0xdd1e16c3, 0x15e0d7f9, 0x50b1b887, 0x2b9f4fd5, 0x625aba82, 0x6a017962, 0x2ec01b9c, 0x15488aa9, 0xd716e740, 0x40055a2c, 0x93d29a22, 0xe32dbf9a, 0x058745b9, 0x3453dc1e, 0xd699296e, 0x496cff6f, 0x1c9f4986, 0xdfe2ed07, 0xb87242d1, 0x19de7eae, 0x053e561a, 0x15ad6f8c, 0x66626c1c, 0x7154c24c, 0xea082b2a, 0x93eb2939, 0x17dcb0f0, 0x58d4f2ae, 0x9ea294fb, 0x52cf564c, 0x9883fe66, 0x2ec40581, 0x763953c3, 0x01d6692e, 0xd3a0c108, 0xa1e7160e, 0xe4f2dfa6, 0x693ed285, 0x74904698, 0x4c2b0edd, 0x4f757656, 0x5d393378, 0xa132234f, 0x3d321c5d, 0xc3f5e194, 0x4b269301, 0xc79f022f, 0x3c997e7e, 0x5e4f9504, 0x3ffafbbd, 0x76f7ad0e, 0x296693f4, 0x3d1fce6f, 0xc61e45be, 0xd3b5ab34, 0xf72bf9b7, 0x1b0434c0, 0x4e72b567, 0x5592a33d, 0xb5229301, 0xcfd2a87f, 0x60aeb767, 0x1814386b, 0x30bcc33d, 0x38a0c07d, 0xfd1606f2, 0xc363519b, 0x589dd390, 0x5479f8e6, 0x1cb8d647, 0x97fd61a9, 0xea7759f4, 0x2d57539d, 0x569a58cf, 0xe84e63ad, 0x462e1b78, 0x6580f87e, 0xf3817914, 0x91da55f4, 0x40a230f3, 0xd1988f35, 0xb6e318d2, 0x3ffa50bc, 0x3d40f021, 0xc3c0bdae, 0x4958c24c, 0x518f36b2, 0x84b1d370, 0x0fedce83, 0x878ddada, 0xf2a279c7, 0x94e01be8, 0x90716f4b, 0x954b8aa3, }; static u_int32_t S8[] = { 0xe216300d, 0xbbddfffc, 0xa7ebdabd, 0x35648095, 0x7789f8b7, 0xe6c1121b, 0x0e241600, 0x052ce8b5, 0x11a9cfb0, 0xe5952f11, 0xece7990a, 0x9386d174, 0x2a42931c, 0x76e38111, 0xb12def3a, 0x37ddddfc, 0xde9adeb1, 0x0a0cc32c, 0xbe197029, 0x84a00940, 0xbb243a0f, 0xb4d137cf, 0xb44e79f0, 0x049eedfd, 0x0b15a15d, 0x480d3168, 0x8bbbde5a, 0x669ded42, 0xc7ece831, 0x3f8f95e7, 0x72df191b, 0x7580330d, 0x94074251, 0x5c7dcdfa, 0xabbe6d63, 0xaa402164, 0xb301d40a, 0x02e7d1ca, 0x53571dae, 0x7a3182a2, 0x12a8ddec, 0xfdaa335d, 0x176f43e8, 0x71fb46d4, 0x38129022, 0xce949ad4, 0xb84769ad, 0x965bd862, 0x82f3d055, 0x66fb9767, 0x15b80b4e, 0x1d5b47a0, 0x4cfde06f, 0xc28ec4b8, 0x57e8726e, 0x647a78fc, 0x99865d44, 0x608bd593, 0x6c200e03, 0x39dc5ff6, 0x5d0b00a3, 0xae63aff2, 0x7e8bd632, 0x70108c0c, 0xbbd35049, 0x2998df04, 0x980cf42a, 0x9b6df491, 0x9e7edd53, 0x06918548, 0x58cb7e07, 0x3b74ef2e, 0x522fffb1, 0xd24708cc, 0x1c7e27cd, 0xa4eb215b, 0x3cf1d2e2, 0x19b47a38, 0x424f7618, 0x35856039, 0x9d17dee7, 0x27eb35e6, 0xc9aff67b, 0x36baf5b8, 0x09c467cd, 0xc18910b1, 0xe11dbf7b, 0x06cd1af8, 0x7170c608, 0x2d5e3354, 0xd4de495a, 0x64c6d006, 0xbcc0c62c, 0x3dd00db3, 0x708f8f34, 0x77d51b42, 0x264f620f, 0x24b8d2bf, 0x15c1b79e, 0x46a52564, 0xf8d7e54e, 0x3e378160, 0x7895cda5, 0x859c15a5, 0xe6459788, 0xc37bc75f, 0xdb07ba0c, 0x0676a3ab, 0x7f229b1e, 0x31842e7b, 0x24259fd7, 0xf8bef472, 0x835ffcb8, 0x6df4c1f2, 0x96f5b195, 0xfd0af0fc, 0xb0fe134c, 0xe2506d3d, 0x4f9b12ea, 0xf215f225, 0xa223736f, 0x9fb4c428, 0x25d04979, 0x34c713f8, 0xc4618187, 0xea7a6e98, 0x7cd16efc, 0x1436876c, 0xf1544107, 0xbedeee14, 0x56e9af27, 0xa04aa441, 0x3cf7c899, 0x92ecbae6, 0xdd67016d, 0x151682eb, 0xa842eedf, 0xfdba60b4, 0xf1907b75, 0x20e3030f, 0x24d8c29e, 0xe139673b, 0xefa63fb8, 0x71873054, 0xb6f2cf3b, 0x9f326442, 0xcb15a4cc, 0xb01a4504, 0xf1e47d8d, 0x844a1be5, 0xbae7dfdc, 0x42cbda70, 0xcd7dae0a, 0x57e85b7a, 0xd53f5af6, 0x20cf4d8c, 0xcea4d428, 0x79d130a4, 0x3486ebfb, 0x33d3cddc, 0x77853b53, 0x37effcb5, 0xc5068778, 0xe580b3e6, 0x4e68b8f4, 0xc5c8b37e, 0x0d809ea2, 0x398feb7c, 0x132a4f94, 0x43b7950e, 0x2fee7d1c, 0x223613bd, 0xdd06caa2, 0x37df932b, 0xc4248289, 0xacf3ebc3, 0x5715f6b7, 0xef3478dd, 0xf267616f, 0xc148cbe4, 0x9052815e, 0x5e410fab, 0xb48a2465, 0x2eda7fa4, 0xe87b40e4, 0xe98ea084, 0x5889e9e1, 0xefd390fc, 0xdd07d35b, 0xdb485694, 0x38d7e5b2, 0x57720101, 0x730edebc, 0x5b643113, 0x94917e4f, 0x503c2fba, 0x646f1282, 0x7523d24a, 0xe0779695, 0xf9c17a8f, 0x7a5b2121, 0xd187b896, 0x29263a4d, 0xba510cdf, 0x81f47c9f, 0xad1163ed, 0xea7b5965, 0x1a00726e, 0x11403092, 0x00da6d77, 0x4a0cdd61, 0xad1f4603, 0x605bdfb0, 0x9eedc364, 0x22ebe6a8, 0xcee7d28a, 0xa0e736a0, 0x5564a6b9, 0x10853209, 0xc7eb8f37, 0x2de705ca, 0x8951570f, 0xdf09822b, 0xbd691a6c, 0xaa12e4f2, 0x87451c0f, 0xe0f6a27a, 0x3ada4819, 0x4cf1764f, 0x0d771c2b, 0x67cdb156, 0x350d8384, 0x5938fa0f, 0x42399ef3, 0x36997b07, 0x0e84093d, 0x4aa93e61, 0x8360d87b, 0x1fa98b0c, 0x1149382c, 0xe97625a5, 0x0614d1b7, 0x0e25244b, 0x0c768347, 0x589e8d82, 0x0d2059d1, 0xa466bb1e, 0xf8da0a82, 0x04f19130, 0xba6e4ec0, 0x99265164, 0x1ee7230d, 0x50b2ad80, 0xeaee6801, 0x8db2a283, 0xea8bf59e, }; Index: head/sys/crypto/cast128/cast128.h =================================================================== --- head/sys/crypto/cast128/cast128.h (revision 62586) +++ head/sys/crypto/cast128/cast128.h (revision 62587) @@ -1,62 +1,63 @@ +/* $FreeBSD$ */ +/* $KAME: cast128.h,v 1.4 2000/06/14 10:41:16 itojun Exp $ */ + /* * heavily modified by Tomomi Suzuki */ /* * The CAST-128 Encryption Algorithm (RFC 2144) * * original implementation * 1997/08/21 */ /* * Copyright (C) 1997 Hideo "Sir MANMOS" Morishita * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY Hideo "Sir MaNMOS" Morishita ``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 Hideo "Sir MaNMOS" Morishita BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ #ifndef RFC2144_CAST_128_H -#define RFC2144_CAST_128_H +#define RFC2144_CAST_128_H #include #include #define CAST128_ENCRYPT 1 #define CAST128_DECRYPT 0 extern void set_cast128_subkey __P((u_int32_t *, u_int8_t *)); extern void cast128_encrypt_round16 __P((u_int8_t *, const u_int8_t *, u_int32_t *)); extern void cast128_decrypt_round16 __P((u_int8_t *, const u_int8_t *, u_int32_t *)); extern void cast128_encrypt_round12 __P((u_int8_t *, const u_int8_t *, u_int32_t *)); extern void cast128_decrypt_round12 __P((u_int8_t *, const u_int8_t *, u_int32_t *)); -extern void cast128_cbc_process __P((struct mbuf *, size_t, size_t, +extern int cast128_cbc_process __P((struct mbuf *, size_t, size_t, u_int32_t *, u_int8_t *, size_t, int)); #endif Index: head/sys/crypto/cast128/cast128_cbc.c =================================================================== --- head/sys/crypto/cast128/cast128_cbc.c (revision 62586) +++ head/sys/crypto/cast128/cast128_cbc.c (revision 62587) @@ -1,219 +1,222 @@ +/* $FreeBSD$ */ +/* $KAME: cast128_cbc.c,v 1.4 2000/06/14 10:41:17 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* * based on sys/crypto/des/des_cbc.c, rewrote by Tomomi Suzuki */ #include #include #include #include +#define panic(x) do { printf(x); return EINVAL; } while (0) -void +int cast128_cbc_process(m0, skip, length, subkey, iv, keylen, mode) struct mbuf *m0; size_t skip; size_t length; u_int32_t *subkey; u_int8_t *iv; size_t keylen; int mode; { struct mbuf *m; u_int8_t inbuf[8], outbuf[8]; size_t off; /* sanity check */ if (m0->m_pkthdr.len < skip) { printf("cast128_cbc_process: mbuf length < skip\n"); - return; + return EINVAL; } if (m0->m_pkthdr.len < length) { printf("cast128_cbc_process: mbuf length < encrypt length\n"); - return; + return EINVAL; } if (m0->m_pkthdr.len < skip + length) { printf("cast128_cbc_process: " "mbuf length < skip + encrypt length\n"); - return; + return EINVAL; } if (length % 8) { printf("cast128_cbc_process: length is not multiple of 8\n"); - return; + return EINVAL; } m = m0; off = 0; /* skip over the header */ while (skip) { if (!m) panic("cast128_cbc_process: mbuf chain?\n"); if (m->m_len <= skip) { skip -= m->m_len; m = m->m_next; off = 0; } else { off = skip; skip = 0; } } /* copy iv into outbuf for XOR (encrypt) */ bcopy(iv, outbuf, 8); /* * encrypt/decrypt packet */ while (length > 0) { int i; if (!m) panic("cast128_cbc_process: mbuf chain?\n"); /* * copy the source into input buffer. * don't update off or m, since we need to use them * later. */ if (off + 8 <= m->m_len) bcopy(mtod(m, u_int8_t *)+off, inbuf, 8); else { struct mbuf *n; size_t noff; u_int8_t *p, *in; n = m; noff = off; p = mtod(n, u_int8_t *) + noff; in = inbuf; while (in - inbuf < 8) { if (!p) { panic("cast128_cbc_process: " "mbuf chain?\n"); } *in++ = *p++; noff++; if (noff < n->m_len) continue; do { n = n->m_next; } while (n && !n->m_len); noff = 0; if (n) p = mtod(n, u_int8_t *); else p = NULL; } } /* encrypt/decrypt */ switch (mode) { case CAST128_ENCRYPT: /* XOR */ for (i = 0; i < 8; i++) inbuf[i] ^= outbuf[i]; /* encrypt */ if (keylen <= 80/8) cast128_encrypt_round12(outbuf, inbuf, subkey); else cast128_encrypt_round16(outbuf, inbuf, subkey); break; case CAST128_DECRYPT: /* decrypt */ if (keylen <= 80/8) cast128_decrypt_round12(outbuf, inbuf, subkey); else cast128_decrypt_round16(outbuf, inbuf, subkey); /* XOR */ for (i = 0; i < 8; i++) outbuf[i] ^= iv[i]; /* copy inbuf into iv for next XOR */ bcopy(inbuf, iv, 8); break; } /* * copy the output buffer into the result. * need to update off and m. */ if (off + 8 < m->m_len) { bcopy(outbuf, mtod(m, u_int8_t *) + off, 8); off += 8; } else if (off + 8 == m->m_len) { bcopy(outbuf, mtod(m, u_int8_t *) + off, 8); do { m = m->m_next; } while (m && !m->m_len); off = 0; } else { struct mbuf *n; size_t noff; u_int8_t *p, *out; n = m; noff = off; p = mtod(n, u_int8_t *) + noff; out = outbuf; while (out - outbuf < 8) { if (!p) { panic("cast128_cbc_process: " "mbuf chain?\n"); } *p++ = *out++; noff++; if (noff < n->m_len) continue; do { n = n->m_next; } while (n && !n->m_len); noff = 0; if (n) p = mtod(n, u_int8_t *); else p = NULL; } m = n; off = noff; } length -= 8; } -} + return 0; +} Index: head/sys/crypto/cast128/cast128_subkey.h =================================================================== --- head/sys/crypto/cast128/cast128_subkey.h (revision 62586) +++ head/sys/crypto/cast128/cast128_subkey.h (revision 62587) @@ -1,91 +1,92 @@ +/* $FreeBSD$ */ +/* $KAME: cast128_subkey.h,v 1.3 2000/03/27 04:36:30 sumikawa Exp $ */ + /* * heavily modified by Tomomi Suzuki */ /* * The CAST-128 Encryption Algorithm (RFC 2144) * * original implementation * 1997/08/21 */ /* * Copyright (C) 1997 Hideo "Sir MANMOS" Morishita * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY Hideo "Sir MaNMOS" Morishita ``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 Hideo "Sir MaNMOS" Morishita BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ #ifndef RFC2144_CAST_128_SUBKEY_H -#define RFC2144_CAST_128_SUBKEY_H +#define RFC2144_CAST_128_SUBKEY_H -#define x0x1x2x3 buf[0] -#define x4x5x6x7 buf[1] -#define x8x9xAxB buf[2] -#define xCxDxExF buf[3] -#define z0z1z2z3 buf[4] -#define z4z5z6z7 buf[5] -#define z8z9zAzB buf[6] -#define zCzDzEzF buf[7] +#define x0x1x2x3 buf[0] +#define x4x5x6x7 buf[1] +#define x8x9xAxB buf[2] +#define xCxDxExF buf[3] +#define z0z1z2z3 buf[4] +#define z4z5z6z7 buf[5] +#define z8z9zAzB buf[6] +#define zCzDzEzF buf[7] -#define byte0(x) (((x) >> 24)) -#define byte1(x) (((x) >> 16) & 0xff) -#define byte2(x) (((x) >> 8) & 0xff) -#define byte3(x) (((x)) & 0xff) +#define byte0(x) (((x) >> 24)) +#define byte1(x) (((x) >> 16) & 0xff) +#define byte2(x) (((x) >> 8) & 0xff) +#define byte3(x) (((x)) & 0xff) -#define x0 byte0(buf[0]) -#define x1 byte1(buf[0]) -#define x2 byte2(buf[0]) -#define x3 byte3(buf[0]) -#define x4 byte0(buf[1]) -#define x5 byte1(buf[1]) -#define x6 byte2(buf[1]) -#define x7 byte3(buf[1]) -#define x8 byte0(buf[2]) -#define x9 byte1(buf[2]) -#define xA byte2(buf[2]) -#define xB byte3(buf[2]) -#define xC byte0(buf[3]) -#define xD byte1(buf[3]) -#define xE byte2(buf[3]) -#define xF byte3(buf[3]) -#define z0 byte0(buf[4]) -#define z1 byte1(buf[4]) -#define z2 byte2(buf[4]) -#define z3 byte3(buf[4]) -#define z4 byte0(buf[5]) -#define z5 byte1(buf[5]) -#define z6 byte2(buf[5]) -#define z7 byte3(buf[5]) -#define z8 byte0(buf[6]) -#define z9 byte1(buf[6]) -#define zA byte2(buf[6]) -#define zB byte3(buf[6]) -#define zC byte0(buf[7]) -#define zD byte1(buf[7]) -#define zE byte2(buf[7]) -#define zF byte3(buf[7]) +#define x0 byte0(buf[0]) +#define x1 byte1(buf[0]) +#define x2 byte2(buf[0]) +#define x3 byte3(buf[0]) +#define x4 byte0(buf[1]) +#define x5 byte1(buf[1]) +#define x6 byte2(buf[1]) +#define x7 byte3(buf[1]) +#define x8 byte0(buf[2]) +#define x9 byte1(buf[2]) +#define xA byte2(buf[2]) +#define xB byte3(buf[2]) +#define xC byte0(buf[3]) +#define xD byte1(buf[3]) +#define xE byte2(buf[3]) +#define xF byte3(buf[3]) +#define z0 byte0(buf[4]) +#define z1 byte1(buf[4]) +#define z2 byte2(buf[4]) +#define z3 byte3(buf[4]) +#define z4 byte0(buf[5]) +#define z5 byte1(buf[5]) +#define z6 byte2(buf[5]) +#define z7 byte3(buf[5]) +#define z8 byte0(buf[6]) +#define z9 byte1(buf[6]) +#define zA byte2(buf[6]) +#define zB byte3(buf[6]) +#define zC byte0(buf[7]) +#define zD byte1(buf[7]) +#define zE byte2(buf[7]) +#define zF byte3(buf[7]) -#define circular_leftshift(x, y) ( ((x) << (y)) | ((x) >> (32-(y))) ) +#define circular_leftshift(x, y) ( ((x) << (y)) | ((x) >> (32-(y))) ) #endif Index: head/sys/crypto/des/des.h =================================================================== --- head/sys/crypto/des/des.h (revision 62586) +++ head/sys/crypto/des/des.h (revision 62587) @@ -1,280 +1,281 @@ +/* $FreeBSD$ */ +/* $KAME: des.h,v 1.4 2000/06/14 10:41:17 itojun Exp $ */ + /* lib/des/des.h */ /* Copyright (C) 1995-1996 Eric Young (eay@mincom.oz.au) * All rights reserved. * * This file is part of an SSL implementation written * by Eric Young (eay@mincom.oz.au). * The implementation was written so as to conform with Netscapes SSL * specification. This library and applications are * FREE FOR COMMERCIAL AND NON-COMMERCIAL USE * as long as the following conditions are aheared to. * * Copyright remains Eric Young's, and as such any Copyright notices in * the code are not to be removed. If this code is used in a product, * Eric Young should be given attribution as the author of the parts used. * This can be in the form of a textual message at program startup or * in documentation (online or textual) provided with the package. * * 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 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 Eric Young (eay@mincom.oz.au) * * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * The licence and distribution terms for any publically available version or * derivative of this code cannot be changed. i.e. this code cannot simply be * copied and put under another distribution licence * [including the GNU Public Licence.] - * - * $FreeBSD$ */ #ifndef HEADER_DES_H -#define HEADER_DES_H +#define HEADER_DES_H #ifdef __cplusplus extern "C" { #endif /* If this is set to 'unsigned int' on a DEC Alpha, this gives about a * %20 speed up (longs are 8 bytes, int's are 4). */ #ifndef DES_LONG -#define DES_LONG unsigned long +#define DES_LONG unsigned long #endif typedef unsigned char des_cblock[8]; typedef struct des_ks_struct { union { des_cblock _; /* make sure things are correct size on machines with * 8 byte longs */ DES_LONG pad[2]; } ks; #undef _ -#define _ ks._ +#define _ ks._ } des_key_schedule[16]; -#define DES_KEY_SZ (sizeof(des_cblock)) -#define DES_SCHEDULE_SZ (sizeof(des_key_schedule)) +#define DES_KEY_SZ (sizeof(des_cblock)) +#define DES_SCHEDULE_SZ (sizeof(des_key_schedule)) -#define DES_ENCRYPT 1 -#define DES_DECRYPT 0 +#define DES_ENCRYPT 1 +#define DES_DECRYPT 0 -#define DES_CBC_MODE 0 -#define DES_PCBC_MODE 1 +#define DES_CBC_MODE 0 +#define DES_PCBC_MODE 1 -#define des_ecb2_encrypt(i,o,k1,k2,e) \ +#define des_ecb2_encrypt(i,o,k1,k2,e) \ des_ecb3_encrypt((i),(o),(k1),(k2),(k1),(e)) -#define des_ede2_cbc_encrypt(i,o,l,k1,k2,iv,e) \ +#define des_ede2_cbc_encrypt(i,o,l,k1,k2,iv,e) \ des_ede3_cbc_encrypt((i),(o),(l),(k1),(k2),(k1),(iv),(e)) -#define des_ede2_cfb64_encrypt(i,o,l,k1,k2,iv,n,e) \ +#define des_ede2_cfb64_encrypt(i,o,l,k1,k2,iv,n,e) \ des_ede3_cfb64_encrypt((i),(o),(l),(k1),(k2),(k1),(iv),(n),(e)) -#define des_ede2_ofb64_encrypt(i,o,l,k1,k2,iv,n) \ +#define des_ede2_ofb64_encrypt(i,o,l,k1,k2,iv,n) \ des_ede3_ofb64_encrypt((i),(o),(l),(k1),(k2),(k1),(iv),(n)) -#define C_Block des_cblock -#define Key_schedule des_key_schedule +#define C_Block des_cblock +#define Key_schedule des_key_schedule #ifdef KERBEROS -#define ENCRYPT DES_ENCRYPT -#define DECRYPT DES_DECRYPT +#define ENCRYPT DES_ENCRYPT +#define DECRYPT DES_DECRYPT #endif -#define KEY_SZ DES_KEY_SZ -#define string_to_key des_string_to_key -#define read_pw_string des_read_pw_string -#define random_key des_random_key -#define pcbc_encrypt des_pcbc_encrypt -#define set_key des_set_key -#define key_sched des_key_sched -#define ecb_encrypt des_ecb_encrypt -#define cbc_encrypt des_cbc_encrypt -#define ncbc_encrypt des_ncbc_encrypt -#define xcbc_encrypt des_xcbc_encrypt -#define cbc_cksum des_cbc_cksum -#define quad_cksum des_quad_cksum +#define KEY_SZ DES_KEY_SZ +#define string_to_key des_string_to_key +#define read_pw_string des_read_pw_string +#define random_key des_random_key +#define pcbc_encrypt des_pcbc_encrypt +#define set_key des_set_key +#define key_sched des_key_sched +#define ecb_encrypt des_ecb_encrypt +#define cbc_encrypt des_cbc_encrypt +#define ncbc_encrypt des_ncbc_encrypt +#define xcbc_encrypt des_xcbc_encrypt +#define cbc_cksum des_cbc_cksum +#define quad_cksum des_quad_cksum /* For compatibility with the MIT lib - eay 20/05/92 */ typedef des_key_schedule bit_64; -#define des_fixup_key_parity des_set_odd_parity -#define des_check_key_parity check_parity +#define des_fixup_key_parity des_set_odd_parity +#define des_check_key_parity check_parity extern int des_check_key; /* defaults to false */ extern int des_rw_mode; /* defaults to DES_PCBC_MODE */ /* The next line is used to disable full ANSI prototypes, if your * compiler has problems with the prototypes, make sure this line always * evaluates to true :-) */ #if defined(MSDOS) || defined(__STDC__) #undef NOPROTO #endif #ifndef NOPROTO char *des_options(void); void des_ecb3_encrypt(des_cblock *input,des_cblock *output, des_key_schedule ks1,des_key_schedule ks2, des_key_schedule ks3, int enc); DES_LONG des_cbc_cksum(des_cblock *input,des_cblock *output, long length,des_key_schedule schedule,des_cblock *ivec); /* void des_cbc_encrypt(des_cblock *input,des_cblock *output,long length, des_key_schedule schedule,des_cblock *ivec,int enc); */ -void des_cbc_encrypt(struct mbuf *, size_t, size_t, +int des_cbc_encrypt(struct mbuf *, size_t, size_t, des_key_schedule schedule,des_cblock *ivec, int enc); void des_ncbc_encrypt(des_cblock *input,des_cblock *output,long length, des_key_schedule schedule,des_cblock *ivec,int enc); void des_xcbc_encrypt(des_cblock *input,des_cblock *output,long length, des_key_schedule schedule,des_cblock *ivec, des_cblock *inw,des_cblock *outw,int enc); void des_3cbc_encrypt(des_cblock *input,des_cblock *output,long length, des_key_schedule sk1,des_key_schedule sk2, des_cblock *ivec1,des_cblock *ivec2,int enc); -extern void des_3cbc_process(struct mbuf *, size_t, size_t, +extern int des_3cbc_process(struct mbuf *, size_t, size_t, des_key_schedule *schedule, des_cblock *ivec, int mode); void des_cfb_encrypt(unsigned char *in,unsigned char *out,int numbits, long length,des_key_schedule schedule,des_cblock *ivec,int enc); void des_ecb_encrypt(des_cblock *input,des_cblock *output, des_key_schedule ks,int enc); void des_encrypt(DES_LONG *data,des_key_schedule ks, int enc); void des_encrypt2(DES_LONG *data,des_key_schedule ks, int enc); void des_ede3_cbc_encrypt(des_cblock *input, des_cblock *output, long length, des_key_schedule ks1, des_key_schedule ks2, des_key_schedule ks3, des_cblock *ivec, int enc); void des_ede3_cfb64_encrypt(unsigned char *in, unsigned char *out, long length, des_key_schedule ks1, des_key_schedule ks2, des_key_schedule ks3, des_cblock *ivec, int *num, int encrypt); void des_ede3_ofb64_encrypt(unsigned char *in, unsigned char *out, long length, des_key_schedule ks1, des_key_schedule ks2, des_key_schedule ks3, des_cblock *ivec, int *num); int des_enc_read(int fd,char *buf,int len,des_key_schedule sched, des_cblock *iv); int des_enc_write(int fd,char *buf,int len,des_key_schedule sched, des_cblock *iv); #ifdef PERL5 char *des_crypt(const char *buf,const char *salt); #else /* some stupid compilers complain because I have declared char instead * of const char */ #if 1 char *crypt(const char *buf,const char *salt); #else char *crypt(); #endif #endif void des_ofb_encrypt(unsigned char *in,unsigned char *out, int numbits,long length,des_key_schedule schedule,des_cblock *ivec); void des_pcbc_encrypt(des_cblock *input,des_cblock *output,long length, des_key_schedule schedule,des_cblock *ivec,int enc); DES_LONG des_quad_cksum(des_cblock *input,des_cblock *output, long length,int out_count,des_cblock *seed); void des_random_seed(des_cblock key); void des_random_key(des_cblock ret); int des_read_password(des_cblock *key,char *prompt,int verify); int des_read_2passwords(des_cblock *key1,des_cblock *key2, char *prompt,int verify); int des_read_pw_string(char *buf,int length,char *prompt,int verify); void des_set_odd_parity(des_cblock *key); int des_is_weak_key(des_cblock *key); int des_set_key(des_cblock *key,des_key_schedule schedule); int des_key_sched(des_cblock *key,des_key_schedule schedule); void des_string_to_key(char *str,des_cblock *key); void des_string_to_2keys(char *str,des_cblock *key1,des_cblock *key2); void des_cfb64_encrypt(unsigned char *in, unsigned char *out, long length, des_key_schedule schedule, des_cblock *ivec, int *num, int enc); void des_ofb64_encrypt(unsigned char *in, unsigned char *out, long length, des_key_schedule schedule, des_cblock *ivec, int *num); /* Extra functions from Mark Murray */ /* void des_cblock_print_file(des_cblock *cb, FILE *fp); */ /* The following functions are not in the normal unix build or the * SSLeay build. When using the SSLeay build, use RAND_seed() * and RAND_bytes() instead. */ int des_new_random_key(des_cblock *key); void des_init_random_number_generator(des_cblock *key); void des_set_random_generator_seed(des_cblock *key); void des_set_sequence_number(des_cblock new_sequence_number); void des_generate_random_block(des_cblock *block); #else char *des_options(); void des_ecb3_encrypt(); DES_LONG des_cbc_cksum(); void des_cbc_encrypt(); void des_ncbc_encrypt(); void des_xcbc_encrypt(); void des_3cbc_encrypt(); void des_cfb_encrypt(); void des_ede3_cfb64_encrypt(); void des_ede3_ofb64_encrypt(); void des_ecb_encrypt(); void des_encrypt(); void des_encrypt2(); void des_ede3_cbc_encrypt(); int des_enc_read(); int des_enc_write(); #ifdef PERL5 char *des_crypt(); #else char *crypt(); #endif void des_ofb_encrypt(); void des_pcbc_encrypt(); DES_LONG des_quad_cksum(); void des_random_seed(); void des_random_key(); int des_read_password(); int des_read_2passwords(); int des_read_pw_string(); void des_set_odd_parity(); int des_is_weak_key(); int des_set_key(); int des_key_sched(); void des_string_to_key(); void des_string_to_2keys(); void des_cfb64_encrypt(); void des_ofb64_encrypt(); /* Extra functions from Mark Murray */ void des_cblock_print_file(); /* The following functions are not in the normal unix build or the * SSLeay build. When using the SSLeay build, use RAND_seed() * and RAND_bytes() instead. */ #ifdef FreeBSD int des_new_random_key(); void des_init_random_number_generator(); void des_set_random_generator_seed(); void des_set_sequence_number(); void des_generate_random_block(); #endif #endif #ifdef __cplusplus } #endif #endif Index: head/sys/crypto/des/des_3cbc.c =================================================================== --- head/sys/crypto/des/des_3cbc.c (revision 62586) +++ head/sys/crypto/des/des_3cbc.c (revision 62587) @@ -1,246 +1,250 @@ +/* $FreeBSD$ */ +/* $KAME: des_3cbc.c,v 1.4 2000/06/14 10:41:17 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* * based on sys/crypto/des/des_cbc.c, rewrote by Tomomi Suzuki */ #include +#define panic(x) do { printf(x); return EINVAL; } while (0) -void des_3cbc_process(m0, skip, length, schedule, ivec, mode) +int des_3cbc_process(m0, skip, length, schedule, ivec, mode) struct mbuf *m0; size_t skip; size_t length; des_key_schedule *schedule; des_cblock (*ivec); int mode; { u_int8_t inbuf[8], outbuf[8]; struct mbuf *m; size_t off; DES_LONG tin0, tin1; DES_LONG tout0, tout1; DES_LONG tin[2]; DES_LONG xor0 = 0, xor1 = 0; u_int8_t *iv; u_int8_t *in, *out; /* sanity check */ if (m0->m_pkthdr.len < skip) { printf("des_3cbc_process: mbuf length < skip\n"); - return; + return EINVAL; } if (m0->m_pkthdr.len < length) { printf("des_3cbc_process: mbuf length < encrypt length\n"); - return; + return EINVAL; } if (m0->m_pkthdr.len < skip + length) { printf("des_3cbc_process: mbuf length < " "skip + encrypt length\n"); - return; + return EINVAL; } if (length % 8) { printf("des_3cbc_process: length(%lu) is not multiple of 8\n", (u_long)length); - return; + return EINVAL; } m = m0; off = 0; /* skip over the header */ while (skip) { if (!m) panic("des_3cbc_process: mbuf chain?\n"); if (m->m_len <= skip) { skip -= m->m_len; m = m->m_next; off = 0; } else { off = skip; skip = 0; } } /* initialize */ tin0 = tin1 = tout0 = tout1 = 0; tin[0] = tin[1] = 0; switch (mode) { case DES_ENCRYPT: iv = (u_int8_t *)ivec; c2l(iv, tout0); c2l(iv, tout1); break; case DES_DECRYPT: xor0 = xor1 = 0; iv = (u_int8_t *)ivec; c2l(iv, xor0); c2l(iv, xor1); break; } /* * encrypt/decrypt packet */ while (length > 0) { if (!m) panic("des_3cbc_process: mbuf chain?\n"); /* * copy the source into input buffer. * don't update off or m, since we need to use them * later. */ if (off + 8 <= m->m_len) bcopy(mtod(m, u_int8_t *) + off, &inbuf[0], 8); else { struct mbuf *n; size_t noff; u_int8_t *p; u_int8_t *in; n = m; noff = off; p = mtod(n, u_int8_t *) + noff; in = &inbuf[0]; while (in - &inbuf[0] < 8) { if (!p) { panic("des_3cbc_process: " "mbuf chain?\n"); } *in++ = *p++; noff++; if (noff < n->m_len) continue; do { n = n->m_next; } while (n && !n->m_len); noff = 0; if (n) p = mtod(n, u_int8_t *) + noff; else p = NULL; } } /* encrypt/decrypt */ switch (mode) { case DES_ENCRYPT: in = &inbuf[0]; out = &outbuf[0]; c2l(in, tin0); c2l(in, tin1); /* XOR */ tin0 ^= tout0; tin[0] = tin0; tin1 ^= tout1; tin[1] = tin1; des_encrypt((DES_LONG *)tin, schedule[0], DES_ENCRYPT); des_encrypt((DES_LONG *)tin, schedule[1], DES_DECRYPT); des_encrypt((DES_LONG *)tin, schedule[2], DES_ENCRYPT); tout0 = tin[0]; l2c(tout0, out); tout1 = tin[1]; l2c(tout1, out); break; case DES_DECRYPT: in = &inbuf[0]; out = &outbuf[0]; c2l(in, tin0); tin[0] = tin0; c2l(in, tin1); tin[1] = tin1; des_encrypt((DES_LONG *)tin, schedule[2], DES_DECRYPT); des_encrypt((DES_LONG *)tin, schedule[1], DES_ENCRYPT); des_encrypt((DES_LONG *)tin, schedule[0], DES_DECRYPT); /* XOR */ tout0 = tin[0] ^ xor0; tout1 = tin[1] ^ xor1; l2c(tout0, out); l2c(tout1, out); /* for next iv */ xor0 = tin0; xor1 = tin1; break; } /* * copy the output buffer int the result. * need to update off and m. */ if (off + 8 < m->m_len) { bcopy(&outbuf[0], mtod(m, u_int8_t *) + off, 8); off += 8; } else if (off + 8 == m->m_len) { bcopy(&outbuf[0], mtod(m, u_int8_t *) + off, 8); do { m = m->m_next; } while (m && !m->m_len); off = 0; } else { struct mbuf *n; size_t noff; u_int8_t *p; u_int8_t *out; n = m; noff = off; p = mtod(n, u_int8_t *) + noff; out = &outbuf[0]; while (out - &outbuf[0] < 8) { if (!p) { panic("des_3cbc_process: " "mbuf chain?\n"); } *p++ = *out++; noff++; if (noff < n->m_len) continue; do { n = n->m_next; } while (n && !n->m_len); noff = 0; if (n) p = mtod(n, u_int8_t *) + noff; else p = NULL; } m = n; off = noff; } length -= 8; } + + return 0; } Index: head/sys/crypto/des/des_cbc.c =================================================================== --- head/sys/crypto/des/des_cbc.c (revision 62586) +++ head/sys/crypto/des/des_cbc.c (revision 62587) @@ -1,328 +1,331 @@ +/* $FreeBSD$ */ +/* $KAME: des_cbc.c,v 1.4 2000/06/14 10:41:17 itojun Exp $ */ + /* * heavily modified by Yoshifumi Nishida . * then, completely rewrote by Jun-ichiro itojun Itoh , * 1997. */ /* crypto/des/cbc_enc.c */ /* Copyright (C) 1995-1996 Eric Young (eay@mincom.oz.au) * All rights reserved. * * This file is part of an SSL implementation written * by Eric Young (eay@mincom.oz.au). * The implementation was written so as to conform with Netscapes SSL * specification. This library and applications are * FREE FOR COMMERCIAL AND NON-COMMERCIAL USE * as long as the following conditions are aheared to. * * Copyright remains Eric Young's, and as such any Copyright notices in * the code are not to be removed. If this code is used in a product, * Eric Young should be given attribution as the author of the parts used. * This can be in the form of a textual message at program startup or * in documentation (online or textual) provided with the package. * * 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 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 Eric Young (eay@mincom.oz.au) * * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * The licence and distribution terms for any publically available version or * derivative of this code cannot be changed. i.e. this code cannot simply be * copied and put under another distribution licence * [including the GNU Public Licence.] - * - * $FreeBSD$ */ #include -#define panic(x) {printf(x); return;} +#define panic(x) do {printf(x); return EINVAL;} while (0) -void des_cbc_encrypt(m0, skip, length, schedule, ivec, mode) +int des_cbc_encrypt(m0, skip, length, schedule, ivec, mode) struct mbuf *m0; size_t skip; size_t length; des_key_schedule schedule; des_cblock (*ivec); int mode; { u_int8_t inbuf[8], outbuf[8]; struct mbuf *m; size_t off; register DES_LONG tin0, tin1; register DES_LONG tout0, tout1; DES_LONG tin[2]; u_int8_t *iv; /* sanity checks */ if (m0->m_pkthdr.len < skip) { printf("mbuf length < skip\n"); - return; + return EINVAL; } if (m0->m_pkthdr.len < length) { printf("mbuf length < encrypt length\n"); - return; + return EINVAL; } if (m0->m_pkthdr.len < skip + length) { printf("mbuf length < skip + encrypt length\n"); - return; + return EINVAL; } if (length % 8) { printf("length is not multiple of 8\n"); - return; + return EINVAL; } m = m0; off = 0; /* skip over the header */ while (skip) { if (!m) panic("mbuf chain?\n"); if (m->m_len <= skip) { skip -= m->m_len; m = m->m_next; off = 0; } else { off = skip; skip = 0; } } /* initialize */ tin0 = tin1 = tout0 = tout1 = 0; tin[0] = tin[1] = 0; if (mode == DES_ENCRYPT) { u_int8_t *in, *out; iv = (u_int8_t *)ivec; c2l(iv, tout0); c2l(iv, tout1); while (0 < length) { if (!m) panic("mbuf chain?\n"); /* * copy the source into input buffer. * don't update off or m, since we need to use them * later. */ if (off + 8 <= m->m_len) bcopy(mtod(m, u_int8_t *) + off, &inbuf[0], 8); else { struct mbuf *n; size_t noff; u_int8_t *p; u_int8_t *in; n = m; noff = off; p = mtod(n, u_int8_t *) + noff; in = &inbuf[0]; while (in - &inbuf[0] < 8) { if (!p) panic("mbuf chain?\n"); - + *in++ = *p++; noff++; if (noff < n->m_len) continue; do { n = n->m_next; } while (n && ! n->m_len); noff = 0; if (n) p = mtod(n, u_int8_t *) + noff; else p = NULL; } } in = &inbuf[0]; out = &outbuf[0]; c2l(in, tin0); c2l(in, tin1); tin0 ^= tout0; tin[0] = tin0; tin1 ^= tout1; tin[1] = tin1; des_encrypt((DES_LONG *)tin, schedule, DES_ENCRYPT); tout0 = tin[0]; l2c(tout0, out); tout1 = tin[1]; l2c(tout1, out); /* * copy the output buffer into the result. * need to update off and m. */ if (off + 8 < m->m_len) { bcopy(&outbuf[0], mtod(m, u_int8_t *) + off, 8); off += 8; } else if (off + 8 == m->m_len) { bcopy(&outbuf[0], mtod(m, u_int8_t *) + off, 8); do { m = m->m_next; } while (m && ! m->m_len); off = 0; } else { struct mbuf *n; size_t noff; u_int8_t *p; u_int8_t *out; n = m; noff = off; p = mtod(n, u_int8_t *) + noff; out = &outbuf[0]; while (out - &outbuf[0] < 8) { if (!p) panic("mbuf chain?"); *p++ = *out++; noff++; if (noff < n->m_len) continue; do { n = n->m_next; } while (n && ! n->m_len); noff = 0; if (n) p = mtod(n, u_int8_t *) + noff; else p = NULL; } m = n; off = noff; } length -= 8; } } else if (mode == DES_DECRYPT) { register DES_LONG xor0, xor1; u_int8_t *in, *out; xor0 = xor1 = 0; iv = (u_int8_t *)ivec; c2l(iv, xor0); c2l(iv, xor1); while (0 < length) { if (!m) panic("mbuf chain?\n"); /* * copy the source into input buffer. * don't update off or m, since we need to use them * later. */ if (off + 8 <= m->m_len) bcopy(mtod(m, u_int8_t *) + off, &inbuf[0], 8); else { struct mbuf *n; size_t noff; u_int8_t *p; u_int8_t *in; n = m; noff = off; p = mtod(n, u_int8_t *) + noff; in = &inbuf[0]; while (in - &inbuf[0] < 8) { if (!p) panic("mbuf chain?\n"); *in++ = *p++; noff++; if (noff < n->m_len) continue; do { n = n->m_next; } while (n && ! n->m_len); noff = 0; if (n) p = mtod(n, u_int8_t *) + noff; else p = NULL; } } in = &inbuf[0]; out = &outbuf[0]; c2l(in, tin0); tin[0] = tin0; c2l(in, tin1); tin[1] = tin1; des_encrypt((DES_LONG *)tin, schedule, DES_DECRYPT); tout0 = tin[0] ^ xor0; tout1 = tin[1] ^ xor1; l2c(tout0, out); l2c(tout1, out); xor0 = tin0; xor1 = tin1; /* * copy the output buffer into the result. * need to update off and m. */ if (off + 8 < m->m_len) { bcopy(&outbuf[0], mtod(m, u_int8_t *) + off, 8); off += 8; } else if (off + 8 == m->m_len) { bcopy(&outbuf[0], mtod(m, u_int8_t *) + off, 8); do { m = m->m_next; } while (m && ! m->m_len); off = 0; } else { struct mbuf *n; size_t noff; u_int8_t *p; u_int8_t *out; n = m; noff = off; p = mtod(n, u_int8_t *) + noff; out = &outbuf[0]; while (out - &outbuf[0] < 8) { if (!p) panic("mbuf chain?\n"); *p++ = *out++; noff++; if (noff < n->m_len) continue; do { n = n->m_next; } while (n && ! n->m_len); noff = 0; if (n) p = mtod(n, u_int8_t *) + noff; else p = NULL; } m = n; off = noff; } length -= 8; } } + + return 0; } Index: head/sys/crypto/des/des_ecb.c =================================================================== --- head/sys/crypto/des/des_ecb.c (revision 62586) +++ head/sys/crypto/des/des_ecb.c (revision 62587) @@ -1,231 +1,232 @@ +/* $FreeBSD$ */ +/* $KAME: des_ecb.c,v 1.3 2000/03/27 04:36:33 sumikawa Exp $ */ + /* crypto/des/ecb_enc.c */ /* Copyright (C) 1995-1996 Eric Young (eay@mincom.oz.au) * All rights reserved. * * This file is part of an SSL implementation written * by Eric Young (eay@mincom.oz.au). * The implementation was written so as to conform with Netscapes SSL * specification. This library and applications are * FREE FOR COMMERCIAL AND NON-COMMERCIAL USE * as long as the following conditions are aheared to. * * Copyright remains Eric Young's, and as such any Copyright notices in * the code are not to be removed. If this code is used in a product, * Eric Young should be given attribution as the author of the parts used. * This can be in the form of a textual message at program startup or * in documentation (online or textual) provided with the package. * * 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 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 Eric Young (eay@mincom.oz.au) * * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * The licence and distribution terms for any publically available version or * derivative of this code cannot be changed. i.e. this code cannot simply be * copied and put under another distribution licence * [including the GNU Public Licence.] - * - * $FreeBSD$ */ #include #include char *libdes_version="libdes v 3.24 - 20-Apr-1996 - eay"; char *DES_version="DES part of SSLeay 0.6.4 30-Aug-1996"; char *des_options() { #ifdef DES_PTR if (sizeof(DES_LONG) != sizeof(long)) return("des(ptr,int)"); else return("des(ptr,long)"); #else if (sizeof(DES_LONG) != sizeof(long)) return("des(idx,int)"); else return("des(idx,long)"); #endif } - + void des_ecb_encrypt(input, output, ks, encrypt) des_cblock (*input); des_cblock (*output); des_key_schedule ks; int encrypt; { register DES_LONG l; register unsigned char *in,*out; DES_LONG ll[2]; in=(unsigned char *)input; out=(unsigned char *)output; c2l(in,l); ll[0]=l; c2l(in,l); ll[1]=l; des_encrypt(ll,ks,encrypt); l=ll[0]; l2c(l,out); l=ll[1]; l2c(l,out); l=ll[0]=ll[1]=0; } void des_encrypt(data, ks, encrypt) DES_LONG *data; des_key_schedule ks; int encrypt; { register DES_LONG l,r,t,u; #ifdef DES_PTR register unsigned char *des_SP=(unsigned char *)des_SPtrans; #endif #ifdef undef union fudge { DES_LONG l; unsigned short s[2]; unsigned char c[4]; } U,T; #endif register int i; register DES_LONG *s; u=data[0]; r=data[1]; IP(u,r); /* Things have been modified so that the initial rotate is * done outside the loop. This required the * des_SPtrans values in sp.h to be rotated 1 bit to the right. * One perl script later and things have a 5% speed up on a sparc2. * Thanks to Richard Outerbridge <71755.204@CompuServe.COM> * for pointing this out. */ l=(r<<1)|(r>>31); r=(u<<1)|(u>>31); /* clear the top bits on machines with 8byte longs */ l&=0xffffffffL; r&=0xffffffffL; s=(DES_LONG *)ks; /* I don't know if it is worth the effort of loop unrolling the * inner loop */ if (encrypt) { for (i=0; i<32; i+=8) { D_ENCRYPT(l,r,i+0); /* 1 */ D_ENCRYPT(r,l,i+2); /* 2 */ D_ENCRYPT(l,r,i+4); /* 3 */ D_ENCRYPT(r,l,i+6); /* 4 */ } } else { for (i=30; i>0; i-=8) { D_ENCRYPT(l,r,i-0); /* 16 */ D_ENCRYPT(r,l,i-2); /* 15 */ D_ENCRYPT(l,r,i-4); /* 14 */ D_ENCRYPT(r,l,i-6); /* 13 */ } } l=(l>>1)|(l<<31); r=(r>>1)|(r<<31); /* clear the top bits on machines with 8byte longs */ l&=0xffffffffL; r&=0xffffffffL; FP(r,l); data[0]=l; data[1]=r; l=r=t=u=0; } void des_encrypt2(data, ks, encrypt) DES_LONG *data; des_key_schedule ks; int encrypt; { register DES_LONG l,r,t,u; #ifdef DES_PTR register unsigned char *des_SP=(unsigned char *)des_SPtrans; #endif #ifdef undef union fudge { DES_LONG l; unsigned short s[2]; unsigned char c[4]; } U,T; #endif register int i; register DES_LONG *s; u=data[0]; r=data[1]; /* Things have been modified so that the initial rotate is * done outside the loop. This required the * des_SPtrans values in sp.h to be rotated 1 bit to the right. * One perl script later and things have a 5% speed up on a sparc2. * Thanks to Richard Outerbridge <71755.204@CompuServe.COM> * for pointing this out. */ l=(r<<1)|(r>>31); r=(u<<1)|(u>>31); /* clear the top bits on machines with 8byte longs */ l&=0xffffffffL; r&=0xffffffffL; s=(DES_LONG *)ks; /* I don't know if it is worth the effort of loop unrolling the * inner loop */ if (encrypt) { for (i=0; i<32; i+=8) { D_ENCRYPT(l,r,i+0); /* 1 */ D_ENCRYPT(r,l,i+2); /* 2 */ D_ENCRYPT(l,r,i+4); /* 3 */ D_ENCRYPT(r,l,i+6); /* 4 */ } } else { for (i=30; i>0; i-=8) { D_ENCRYPT(l,r,i-0); /* 16 */ D_ENCRYPT(r,l,i-2); /* 15 */ D_ENCRYPT(l,r,i-4); /* 14 */ D_ENCRYPT(r,l,i-6); /* 13 */ } } l=(l>>1)|(l<<31); r=(r>>1)|(r<<31); /* clear the top bits on machines with 8byte longs */ l&=0xffffffffL; r&=0xffffffffL; data[0]=l; data[1]=r; l=r=t=u=0; } Index: head/sys/crypto/des/des_locl.h =================================================================== --- head/sys/crypto/des/des_locl.h (revision 62586) +++ head/sys/crypto/des/des_locl.h (revision 62587) @@ -1,347 +1,348 @@ +/* $FreeBSD$ */ +/* $KAME: des_locl.h,v 1.4 2000/03/27 04:43:46 sumikawa Exp $ */ + /* lib/des/des_locl.h */ /* Copyright (C) 1995-1996 Eric Young (eay@mincom.oz.au) * All rights reserved. * * This file is part of an SSL implementation written * by Eric Young (eay@mincom.oz.au). * The implementation was written so as to conform with Netscapes SSL * specification. This library and applications are * FREE FOR COMMERCIAL AND NON-COMMERCIAL USE * as long as the following conditions are aheared to. * * Copyright remains Eric Young's, and as such any Copyright notices in * the code are not to be removed. If this code is used in a product, * Eric Young should be given attribution as the author of the parts used. * This can be in the form of a textual message at program startup or * in documentation (online or textual) provided with the package. * * 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 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 Eric Young (eay@mincom.oz.au) * * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * The licence and distribution terms for any publically available version or * derivative of this code cannot be changed. i.e. this code cannot simply be * copied and put under another distribution licence * [including the GNU Public Licence.] - * - * $FreeBSD$ */ /* WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * * Always modify des_locl.org since des_locl.h is automatically generated from * it during SSLeay configuration. * * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING */ #include #include #include #include #ifndef HEADER_DES_LOCL_H -#define HEADER_DES_LOCL_H +#define HEADER_DES_LOCL_H #if defined(WIN32) || defined(WIN16) #ifndef MSDOS -#define MSDOS +#define MSDOS #endif #endif /* #include #include #ifndef MSDOS #include #endif */ #include /* the following is tweaked from a config script, that is why it is a * protected undef/define */ #ifndef DES_PTR #undef DES_PTR #endif #ifdef MSDOS /* Visual C++ 2.1 (Windows NT/95) */ #include #include #include #include #ifndef RAND -#define RAND +#define RAND #endif #undef NOPROTO #endif #if !defined(_KERNEL) && (defined(__STDC__) || defined(VMS) || defined(M_XENIX) || defined(MSDOS)) #ifndef __NetBSD__ #include #endif #endif #ifdef __NetBSD__ #include #endif #ifndef RAND -#define RAND +#define RAND #endif #ifdef linux #undef RAND #endif #ifdef MSDOS -#define getpid() 2 -#define RAND +#define getpid() 2 +#define RAND #undef NOPROTO #endif #if defined(NOCONST) -#define const +#define const #endif #ifdef __STDC__ #undef NOPROTO #endif #ifdef RAND -#define srandom(s) srand(s) -#define random rand +#define srandom(s) srand(s) +#define random rand #endif -#define ITERATIONS 16 -#define HALF_ITERATIONS 8 +#define ITERATIONS 16 +#define HALF_ITERATIONS 8 /* used in des_read and des_write */ -#define MAXWRITE (1024*16) -#define BSIZE (MAXWRITE+4) +#define MAXWRITE (1024*16) +#define BSIZE (MAXWRITE+4) -#define c2l(c,l) (l =((DES_LONG)(*((c)++))) , \ +#define c2l(c,l) (l =((DES_LONG)(*((c)++))) , \ l|=((DES_LONG)(*((c)++)))<< 8L, \ l|=((DES_LONG)(*((c)++)))<<16L, \ l|=((DES_LONG)(*((c)++)))<<24L) /* NOTE - c is not incremented as per c2l */ -#define c2ln(c,l1,l2,n) { \ +#define c2ln(c,l1,l2,n) { \ c+=n; \ l1=l2=0; \ switch (n) { \ case 8: l2 =((DES_LONG)(*(--(c))))<<24L; \ case 7: l2|=((DES_LONG)(*(--(c))))<<16L; \ case 6: l2|=((DES_LONG)(*(--(c))))<< 8L; \ case 5: l2|=((DES_LONG)(*(--(c)))); \ case 4: l1 =((DES_LONG)(*(--(c))))<<24L; \ case 3: l1|=((DES_LONG)(*(--(c))))<<16L; \ case 2: l1|=((DES_LONG)(*(--(c))))<< 8L; \ case 1: l1|=((DES_LONG)(*(--(c)))); \ } \ } -#define l2c(l,c) (*((c)++)=(unsigned char)(((l) )&0xff), \ +#define l2c(l,c) (*((c)++)=(unsigned char)(((l) )&0xff), \ *((c)++)=(unsigned char)(((l)>> 8L)&0xff), \ *((c)++)=(unsigned char)(((l)>>16L)&0xff), \ *((c)++)=(unsigned char)(((l)>>24L)&0xff)) /* replacements for htonl and ntohl since I have no idea what to do * when faced with machines with 8 byte longs. */ -#define HDRSIZE 4 +#define HDRSIZE 4 -#define n2l(c,l) (l =((DES_LONG)(*((c)++)))<<24L, \ +#define n2l(c,l) (l =((DES_LONG)(*((c)++)))<<24L, \ l|=((DES_LONG)(*((c)++)))<<16L, \ l|=((DES_LONG)(*((c)++)))<< 8L, \ l|=((DES_LONG)(*((c)++)))) -#define l2n(l,c) (*((c)++)=(unsigned char)(((l)>>24L)&0xff), \ +#define l2n(l,c) (*((c)++)=(unsigned char)(((l)>>24L)&0xff), \ *((c)++)=(unsigned char)(((l)>>16L)&0xff), \ *((c)++)=(unsigned char)(((l)>> 8L)&0xff), \ *((c)++)=(unsigned char)(((l) )&0xff)) /* NOTE - c is not incremented as per l2c */ -#define l2cn(l1,l2,c,n) { \ +#define l2cn(l1,l2,c,n) { \ c+=n; \ switch (n) { \ case 8: *(--(c))=(unsigned char)(((l2)>>24L)&0xff); \ case 7: *(--(c))=(unsigned char)(((l2)>>16L)&0xff); \ case 6: *(--(c))=(unsigned char)(((l2)>> 8L)&0xff); \ case 5: *(--(c))=(unsigned char)(((l2) )&0xff); \ case 4: *(--(c))=(unsigned char)(((l1)>>24L)&0xff); \ case 3: *(--(c))=(unsigned char)(((l1)>>16L)&0xff); \ case 2: *(--(c))=(unsigned char)(((l1)>> 8L)&0xff); \ case 1: *(--(c))=(unsigned char)(((l1) )&0xff); \ } \ } #if defined(WIN32) #define ROTATE(a,n) (_lrotr(a,n)) #else #define ROTATE(a,n) (((a)>>(n))+((a)<<(32-(n)))) #endif /* The changes to this macro may help or hinder, depending on the * compiler and the achitecture. gcc2 always seems to do well :-). * Inspired by Dana How * DO NOT use the alternative version on machines with 8 byte longs. * It does not seem to work on the Alpha, even when DES_LONG is 4 * bytes, probably an issue of accessing non-word aligned objects :-( */ #ifdef DES_PTR -#define D_ENCRYPT(L,R,S) { \ +#define D_ENCRYPT(L,R,S) { \ u=((R^s[S ])<<2); \ t= R^s[S+1]; \ t=ROTATE(t,2); \ L^= (\ *(DES_LONG *)((unsigned char *)des_SP+0x100+((t )&0xfc))+ \ *(DES_LONG *)((unsigned char *)des_SP+0x300+((t>> 8)&0xfc))+ \ *(DES_LONG *)((unsigned char *)des_SP+0x500+((t>>16)&0xfc))+ \ *(DES_LONG *)((unsigned char *)des_SP+0x700+((t>>24)&0xfc))+ \ *(DES_LONG *)((unsigned char *)des_SP +((u )&0xfc))+ \ *(DES_LONG *)((unsigned char *)des_SP+0x200+((u>> 8)&0xfc))+ \ *(DES_LONG *)((unsigned char *)des_SP+0x400+((u>>16)&0xfc))+ \ *(DES_LONG *)((unsigned char *)des_SP+0x600+((u>>24)&0xfc))); } #else /* original version */ #ifdef undef -#define D_ENCRYPT(L,R,S) \ +#define D_ENCRYPT(L,R,S) \ U.l=R^s[S+1]; \ T.s[0]=((U.s[0]>>4)|(U.s[1]<<12))&0x3f3f; \ T.s[1]=((U.s[1]>>4)|(U.s[0]<<12))&0x3f3f; \ U.l=(R^s[S ])&0x3f3f3f3fL; \ L^= des_SPtrans[1][(T.c[0])]| \ des_SPtrans[3][(T.c[1])]| \ des_SPtrans[5][(T.c[2])]| \ des_SPtrans[7][(T.c[3])]| \ des_SPtrans[0][(U.c[0])]| \ des_SPtrans[2][(U.c[1])]| \ des_SPtrans[4][(U.c[2])]| \ des_SPtrans[6][(U.c[3])]; #else -#define D_ENCRYPT(Q,R,S) {\ +#define D_ENCRYPT(Q,R,S) {\ u=(R^s[S ]); \ t=R^s[S+1]; \ t=ROTATE(t,4); \ Q^= des_SPtrans[1][(t )&0x3f]| \ des_SPtrans[3][(t>> 8L)&0x3f]| \ des_SPtrans[5][(t>>16L)&0x3f]| \ des_SPtrans[7][(t>>24L)&0x3f]| \ des_SPtrans[0][(u )&0x3f]| \ des_SPtrans[2][(u>> 8L)&0x3f]| \ des_SPtrans[4][(u>>16L)&0x3f]| \ des_SPtrans[6][(u>>24L)&0x3f]; } #endif #endif /* IP and FP * The problem is more of a geometric problem that random bit fiddling. 0 1 2 3 4 5 6 7 62 54 46 38 30 22 14 6 8 9 10 11 12 13 14 15 60 52 44 36 28 20 12 4 16 17 18 19 20 21 22 23 58 50 42 34 26 18 10 2 24 25 26 27 28 29 30 31 to 56 48 40 32 24 16 8 0 32 33 34 35 36 37 38 39 63 55 47 39 31 23 15 7 40 41 42 43 44 45 46 47 61 53 45 37 29 21 13 5 48 49 50 51 52 53 54 55 59 51 43 35 27 19 11 3 56 57 58 59 60 61 62 63 57 49 41 33 25 17 9 1 The output has been subject to swaps of the form 0 1 -> 3 1 but the odd and even bits have been put into 2 3 2 0 different words. The main trick is to remember that t=((l>>size)^r)&(mask); r^=t; l^=(t<>(n))^(b))&(m)),\ +#define PERM_OP(a,b,t,n,m) ((t)=((((a)>>(n))^(b))&(m)),\ (b)^=(t),\ (a)^=((t)<<(n))) -#define IP(l,r) \ +#define IP(l,r) \ { \ register DES_LONG tt; \ PERM_OP(r,l,tt, 4,0x0f0f0f0fL); \ PERM_OP(l,r,tt,16,0x0000ffffL); \ PERM_OP(r,l,tt, 2,0x33333333L); \ PERM_OP(l,r,tt, 8,0x00ff00ffL); \ PERM_OP(r,l,tt, 1,0x55555555L); \ } -#define FP(l,r) \ +#define FP(l,r) \ { \ register DES_LONG tt; \ PERM_OP(l,r,tt, 1,0x55555555L); \ PERM_OP(r,l,tt, 8,0x00ff00ffL); \ PERM_OP(l,r,tt, 2,0x33333333L); \ PERM_OP(r,l,tt,16,0x0000ffffL); \ PERM_OP(l,r,tt, 4,0x0f0f0f0fL); \ } #endif /* -#define mbuf2char(i_mbuf, i_index, in) \ +#define mbuf2char(i_mbuf, i_index, in) \ { \ - register int i; \ - struct mbuf *m; \ + register int i; \ + struct mbuf *m; \ char *buf; \ m = i_mbuf; \ for (i = 0; i < 8; i ++){ \ if (i_index + i == m->m_len){ \ m = m->m_next; \ } \ buf = mtod(m, char *); \ in[i] = *(buf + i); \ } -#define char2mbuf(o_mbuf, o_index, out) \ +#define char2mbuf(o_mbuf, o_index, out) \ { \ - register int i; \ - struct mbuf *m; \ + register int i; \ + struct mbuf *m; \ char *buf; \ m = o_mbuf; \ for (i = 0; i < 8; i ++){ \ if (i_index + i == m->m_len){ \ m = m->m_next; \ } \ buf = mtod(m, char *); \ *(buf + i) = out[i]; \ } */ Index: head/sys/crypto/des/des_setkey.c =================================================================== --- head/sys/crypto/des/des_setkey.c (revision 62586) +++ head/sys/crypto/des/des_setkey.c (revision 62587) @@ -1,238 +1,239 @@ +/* $FreeBSD$ */ +/* $KAME: des_setkey.c,v 1.3 2000/03/27 04:36:33 sumikawa Exp $ */ + /* crypto/des/set_key.c */ /* Copyright (C) 1995-1996 Eric Young (eay@mincom.oz.au) * All rights reserved. * * This file is part of an SSL implementation written * by Eric Young (eay@mincom.oz.au). * The implementation was written so as to conform with Netscapes SSL * specification. This library and applications are * FREE FOR COMMERCIAL AND NON-COMMERCIAL USE * as long as the following conditions are aheared to. * * Copyright remains Eric Young's, and as such any Copyright notices in * the code are not to be removed. If this code is used in a product, * Eric Young should be given attribution as the author of the parts used. * This can be in the form of a textual message at program startup or * in documentation (online or textual) provided with the package. * * 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 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 Eric Young (eay@mincom.oz.au) * * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * The licence and distribution terms for any publically available version or * derivative of this code cannot be changed. i.e. this code cannot simply be * copied and put under another distribution licence * [including the GNU Public Licence.] - * - * $FreeBSD$ */ /* set_key.c v 1.4 eay 24/9/91 * 1.4 Speed up by 400% :-) * 1.3 added register declarations. * 1.2 unrolled make_key_sched a bit more * 1.1 added norm_expand_bits * 1.0 First working version */ #include #include #include #ifndef NOPROTO static int check_parity(des_cblock (*key)); #else static int check_parity(); #endif int des_check_key=0; void des_set_odd_parity(key) des_cblock (*key); { int i; for (i=0; i>(n))^(b))&(m)),\ * (b)^=(t),\ * (a)=((a)^((t)<<(n)))) */ -#define HPERM_OP(a,t,n,m) ((t)=((((a)<<(16-(n)))^(a))&(m)),\ +#define HPERM_OP(a,t,n,m) ((t)=((((a)<<(16-(n)))^(a))&(m)),\ (a)=(a)^(t)^(t>>(16-(n)))) /* return 0 if key parity is odd (correct), * return -1 if key parity error, * return -2 if illegal weak key. */ int des_set_key(key, schedule) des_cblock (*key); des_key_schedule schedule; { static int shifts2[16]={0,0,1,1,1,1,1,1,0,1,1,1,1,1,1,0}; register DES_LONG c,d,t,s; register unsigned char *in; register DES_LONG *k; register int i; if (des_check_key) { if (!check_parity(key)) return(-1); if (des_is_weak_key(key)) return(-2); } k=(DES_LONG *)schedule; in=(unsigned char *)key; c2l(in,c); c2l(in,d); /* do PC1 in 60 simple operations */ /* PERM_OP(d,c,t,4,0x0f0f0f0fL); HPERM_OP(c,t,-2, 0xcccc0000L); HPERM_OP(c,t,-1, 0xaaaa0000L); HPERM_OP(c,t, 8, 0x00ff0000L); HPERM_OP(c,t,-1, 0xaaaa0000L); HPERM_OP(d,t,-8, 0xff000000L); HPERM_OP(d,t, 8, 0x00ff0000L); HPERM_OP(d,t, 2, 0x33330000L); d=((d&0x00aa00aaL)<<7L)|((d&0x55005500L)>>7L)|(d&0xaa55aa55L); d=(d>>8)|((c&0xf0000000L)>>4); c&=0x0fffffffL; */ /* I now do it in 47 simple operations :-) * Thanks to John Fletcher (john_fletcher@lccmail.ocf.llnl.gov) * for the inspiration. :-) */ PERM_OP (d,c,t,4,0x0f0f0f0fL); HPERM_OP(c,t,-2,0xcccc0000L); HPERM_OP(d,t,-2,0xcccc0000L); PERM_OP (d,c,t,1,0x55555555L); PERM_OP (c,d,t,8,0x00ff00ffL); PERM_OP (d,c,t,1,0x55555555L); d= (((d&0x000000ffL)<<16L)| (d&0x0000ff00L) | ((d&0x00ff0000L)>>16L)|((c&0xf0000000L)>>4L)); c&=0x0fffffffL; for (i=0; i>2L)|(c<<26L)); d=((d>>2L)|(d<<26L)); } else { c=((c>>1L)|(c<<27L)); d=((d>>1L)|(d<<27L)); } c&=0x0fffffffL; d&=0x0fffffffL; /* could be a few less shifts but I am to lazy at this * point in time to investigate */ s= des_skb[0][ (c )&0x3f ]| des_skb[1][((c>> 6)&0x03)|((c>> 7L)&0x3c)]| des_skb[2][((c>>13)&0x0f)|((c>>14L)&0x30)]| des_skb[3][((c>>20)&0x01)|((c>>21L)&0x06) | ((c>>22L)&0x38)]; t= des_skb[4][ (d )&0x3f ]| des_skb[5][((d>> 7L)&0x03)|((d>> 8L)&0x3c)]| des_skb[6][ (d>>15L)&0x3f ]| des_skb[7][((d>>21L)&0x0f)|((d>>22L)&0x30)]; /* table contained 0213 4657 */ *(k++)=((t<<16L)|(s&0x0000ffffL))&0xffffffffL; s= ((s>>16L)|(t&0xffff0000L)); - + s=(s<<4L)|(s>>28L); *(k++)=s&0xffffffffL; } return(0); } int des_key_sched(key, schedule) des_cblock (*key); des_key_schedule schedule; { return(des_set_key(key,schedule)); } Index: head/sys/crypto/des/podd.h =================================================================== --- head/sys/crypto/des/podd.h (revision 62586) +++ head/sys/crypto/des/podd.h (revision 62587) @@ -1,66 +1,67 @@ +/* $FreeBSD$ */ +/* $KAME: podd.h,v 1.3 2000/03/27 04:36:34 sumikawa Exp $ */ + /* crypto/des/podd.h */ /* Copyright (C) 1995-1996 Eric Young (eay@mincom.oz.au) * All rights reserved. * * This file is part of an SSL implementation written * by Eric Young (eay@mincom.oz.au). * The implementation was written so as to conform with Netscapes SSL * specification. This library and applications are * FREE FOR COMMERCIAL AND NON-COMMERCIAL USE * as long as the following conditions are aheared to. * * Copyright remains Eric Young's, and as such any Copyright notices in * the code are not to be removed. If this code is used in a product, * Eric Young should be given attribution as the author of the parts used. * This can be in the form of a textual message at program startup or * in documentation (online or textual) provided with the package. * * 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 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 Eric Young (eay@mincom.oz.au) * * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * The licence and distribution terms for any publically available version or * derivative of this code cannot be changed. i.e. this code cannot simply be * copied and put under another distribution licence * [including the GNU Public Licence.] - * - * $FreeBSD$ */ static const unsigned char odd_parity[256]={ 1, 1, 2, 2, 4, 4, 7, 7, 8, 8, 11, 11, 13, 13, 14, 14, 16, 16, 19, 19, 21, 21, 22, 22, 25, 25, 26, 26, 28, 28, 31, 31, 32, 32, 35, 35, 37, 37, 38, 38, 41, 41, 42, 42, 44, 44, 47, 47, 49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 59, 59, 61, 61, 62, 62, 64, 64, 67, 67, 69, 69, 70, 70, 73, 73, 74, 74, 76, 76, 79, 79, 81, 81, 82, 82, 84, 84, 87, 87, 88, 88, 91, 91, 93, 93, 94, 94, 97, 97, 98, 98,100,100,103,103,104,104,107,107,109,109,110,110, 112,112,115,115,117,117,118,118,121,121,122,122,124,124,127,127, 128,128,131,131,133,133,134,134,137,137,138,138,140,140,143,143, 145,145,146,146,148,148,151,151,152,152,155,155,157,157,158,158, 161,161,162,162,164,164,167,167,168,168,171,171,173,173,174,174, 176,176,179,179,181,181,182,182,185,185,186,186,188,188,191,191, 193,193,194,194,196,196,199,199,200,200,203,203,205,205,206,206, 208,208,211,211,213,213,214,214,217,217,218,218,220,220,223,223, 224,224,227,227,229,229,230,230,233,233,234,234,236,236,239,239, 241,241,242,242,244,244,247,247,248,248,251,251,253,253,254,254}; Index: head/sys/crypto/des/sk.h =================================================================== --- head/sys/crypto/des/sk.h (revision 62586) +++ head/sys/crypto/des/sk.h (revision 62587) @@ -1,195 +1,196 @@ +/* $FreeBSD$ */ +/* $KAME: sk.h,v 1.3 2000/03/27 04:36:34 sumikawa Exp $ */ + /* crypto/des/sk.h */ /* Copyright (C) 1995-1996 Eric Young (eay@mincom.oz.au) * All rights reserved. * * This file is part of an SSL implementation written * by Eric Young (eay@mincom.oz.au). * The implementation was written so as to conform with Netscapes SSL * specification. This library and applications are * FREE FOR COMMERCIAL AND NON-COMMERCIAL USE * as long as the following conditions are aheared to. * * Copyright remains Eric Young's, and as such any Copyright notices in * the code are not to be removed. If this code is used in a product, * Eric Young should be given attribution as the author of the parts used. * This can be in the form of a textual message at program startup or * in documentation (online or textual) provided with the package. * * 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 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 Eric Young (eay@mincom.oz.au) * * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * The licence and distribution terms for any publically available version or * derivative of this code cannot be changed. i.e. this code cannot simply be * copied and put under another distribution licence * [including the GNU Public Licence.] - * - * $FreeBSD$ */ static const DES_LONG des_skb[8][64]={ { /* for C bits (numbered as per FIPS 46) 1 2 3 4 5 6 */ 0x00000000L,0x00000010L,0x20000000L,0x20000010L, 0x00010000L,0x00010010L,0x20010000L,0x20010010L, 0x00000800L,0x00000810L,0x20000800L,0x20000810L, 0x00010800L,0x00010810L,0x20010800L,0x20010810L, 0x00000020L,0x00000030L,0x20000020L,0x20000030L, 0x00010020L,0x00010030L,0x20010020L,0x20010030L, 0x00000820L,0x00000830L,0x20000820L,0x20000830L, 0x00010820L,0x00010830L,0x20010820L,0x20010830L, 0x00080000L,0x00080010L,0x20080000L,0x20080010L, 0x00090000L,0x00090010L,0x20090000L,0x20090010L, 0x00080800L,0x00080810L,0x20080800L,0x20080810L, 0x00090800L,0x00090810L,0x20090800L,0x20090810L, 0x00080020L,0x00080030L,0x20080020L,0x20080030L, 0x00090020L,0x00090030L,0x20090020L,0x20090030L, 0x00080820L,0x00080830L,0x20080820L,0x20080830L, 0x00090820L,0x00090830L,0x20090820L,0x20090830L, },{ /* for C bits (numbered as per FIPS 46) 7 8 10 11 12 13 */ 0x00000000L,0x02000000L,0x00002000L,0x02002000L, 0x00200000L,0x02200000L,0x00202000L,0x02202000L, 0x00000004L,0x02000004L,0x00002004L,0x02002004L, 0x00200004L,0x02200004L,0x00202004L,0x02202004L, 0x00000400L,0x02000400L,0x00002400L,0x02002400L, 0x00200400L,0x02200400L,0x00202400L,0x02202400L, 0x00000404L,0x02000404L,0x00002404L,0x02002404L, 0x00200404L,0x02200404L,0x00202404L,0x02202404L, 0x10000000L,0x12000000L,0x10002000L,0x12002000L, 0x10200000L,0x12200000L,0x10202000L,0x12202000L, 0x10000004L,0x12000004L,0x10002004L,0x12002004L, 0x10200004L,0x12200004L,0x10202004L,0x12202004L, 0x10000400L,0x12000400L,0x10002400L,0x12002400L, 0x10200400L,0x12200400L,0x10202400L,0x12202400L, 0x10000404L,0x12000404L,0x10002404L,0x12002404L, 0x10200404L,0x12200404L,0x10202404L,0x12202404L, },{ /* for C bits (numbered as per FIPS 46) 14 15 16 17 19 20 */ 0x00000000L,0x00000001L,0x00040000L,0x00040001L, 0x01000000L,0x01000001L,0x01040000L,0x01040001L, 0x00000002L,0x00000003L,0x00040002L,0x00040003L, 0x01000002L,0x01000003L,0x01040002L,0x01040003L, 0x00000200L,0x00000201L,0x00040200L,0x00040201L, 0x01000200L,0x01000201L,0x01040200L,0x01040201L, 0x00000202L,0x00000203L,0x00040202L,0x00040203L, 0x01000202L,0x01000203L,0x01040202L,0x01040203L, 0x08000000L,0x08000001L,0x08040000L,0x08040001L, 0x09000000L,0x09000001L,0x09040000L,0x09040001L, 0x08000002L,0x08000003L,0x08040002L,0x08040003L, 0x09000002L,0x09000003L,0x09040002L,0x09040003L, 0x08000200L,0x08000201L,0x08040200L,0x08040201L, 0x09000200L,0x09000201L,0x09040200L,0x09040201L, 0x08000202L,0x08000203L,0x08040202L,0x08040203L, 0x09000202L,0x09000203L,0x09040202L,0x09040203L, },{ /* for C bits (numbered as per FIPS 46) 21 23 24 26 27 28 */ 0x00000000L,0x00100000L,0x00000100L,0x00100100L, 0x00000008L,0x00100008L,0x00000108L,0x00100108L, 0x00001000L,0x00101000L,0x00001100L,0x00101100L, 0x00001008L,0x00101008L,0x00001108L,0x00101108L, 0x04000000L,0x04100000L,0x04000100L,0x04100100L, 0x04000008L,0x04100008L,0x04000108L,0x04100108L, 0x04001000L,0x04101000L,0x04001100L,0x04101100L, 0x04001008L,0x04101008L,0x04001108L,0x04101108L, 0x00020000L,0x00120000L,0x00020100L,0x00120100L, 0x00020008L,0x00120008L,0x00020108L,0x00120108L, 0x00021000L,0x00121000L,0x00021100L,0x00121100L, 0x00021008L,0x00121008L,0x00021108L,0x00121108L, 0x04020000L,0x04120000L,0x04020100L,0x04120100L, 0x04020008L,0x04120008L,0x04020108L,0x04120108L, 0x04021000L,0x04121000L,0x04021100L,0x04121100L, 0x04021008L,0x04121008L,0x04021108L,0x04121108L, },{ /* for D bits (numbered as per FIPS 46) 1 2 3 4 5 6 */ 0x00000000L,0x10000000L,0x00010000L,0x10010000L, 0x00000004L,0x10000004L,0x00010004L,0x10010004L, 0x20000000L,0x30000000L,0x20010000L,0x30010000L, 0x20000004L,0x30000004L,0x20010004L,0x30010004L, 0x00100000L,0x10100000L,0x00110000L,0x10110000L, 0x00100004L,0x10100004L,0x00110004L,0x10110004L, 0x20100000L,0x30100000L,0x20110000L,0x30110000L, 0x20100004L,0x30100004L,0x20110004L,0x30110004L, 0x00001000L,0x10001000L,0x00011000L,0x10011000L, 0x00001004L,0x10001004L,0x00011004L,0x10011004L, 0x20001000L,0x30001000L,0x20011000L,0x30011000L, 0x20001004L,0x30001004L,0x20011004L,0x30011004L, 0x00101000L,0x10101000L,0x00111000L,0x10111000L, 0x00101004L,0x10101004L,0x00111004L,0x10111004L, 0x20101000L,0x30101000L,0x20111000L,0x30111000L, 0x20101004L,0x30101004L,0x20111004L,0x30111004L, },{ /* for D bits (numbered as per FIPS 46) 8 9 11 12 13 14 */ 0x00000000L,0x08000000L,0x00000008L,0x08000008L, 0x00000400L,0x08000400L,0x00000408L,0x08000408L, 0x00020000L,0x08020000L,0x00020008L,0x08020008L, 0x00020400L,0x08020400L,0x00020408L,0x08020408L, 0x00000001L,0x08000001L,0x00000009L,0x08000009L, 0x00000401L,0x08000401L,0x00000409L,0x08000409L, 0x00020001L,0x08020001L,0x00020009L,0x08020009L, 0x00020401L,0x08020401L,0x00020409L,0x08020409L, 0x02000000L,0x0A000000L,0x02000008L,0x0A000008L, 0x02000400L,0x0A000400L,0x02000408L,0x0A000408L, 0x02020000L,0x0A020000L,0x02020008L,0x0A020008L, 0x02020400L,0x0A020400L,0x02020408L,0x0A020408L, 0x02000001L,0x0A000001L,0x02000009L,0x0A000009L, 0x02000401L,0x0A000401L,0x02000409L,0x0A000409L, 0x02020001L,0x0A020001L,0x02020009L,0x0A020009L, 0x02020401L,0x0A020401L,0x02020409L,0x0A020409L, },{ /* for D bits (numbered as per FIPS 46) 16 17 18 19 20 21 */ 0x00000000L,0x00000100L,0x00080000L,0x00080100L, 0x01000000L,0x01000100L,0x01080000L,0x01080100L, 0x00000010L,0x00000110L,0x00080010L,0x00080110L, 0x01000010L,0x01000110L,0x01080010L,0x01080110L, 0x00200000L,0x00200100L,0x00280000L,0x00280100L, 0x01200000L,0x01200100L,0x01280000L,0x01280100L, 0x00200010L,0x00200110L,0x00280010L,0x00280110L, 0x01200010L,0x01200110L,0x01280010L,0x01280110L, 0x00000200L,0x00000300L,0x00080200L,0x00080300L, 0x01000200L,0x01000300L,0x01080200L,0x01080300L, 0x00000210L,0x00000310L,0x00080210L,0x00080310L, 0x01000210L,0x01000310L,0x01080210L,0x01080310L, 0x00200200L,0x00200300L,0x00280200L,0x00280300L, 0x01200200L,0x01200300L,0x01280200L,0x01280300L, 0x00200210L,0x00200310L,0x00280210L,0x00280310L, 0x01200210L,0x01200310L,0x01280210L,0x01280310L, },{ /* for D bits (numbered as per FIPS 46) 22 23 24 25 27 28 */ 0x00000000L,0x04000000L,0x00040000L,0x04040000L, 0x00000002L,0x04000002L,0x00040002L,0x04040002L, 0x00002000L,0x04002000L,0x00042000L,0x04042000L, 0x00002002L,0x04002002L,0x00042002L,0x04042002L, 0x00000020L,0x04000020L,0x00040020L,0x04040020L, 0x00000022L,0x04000022L,0x00040022L,0x04040022L, 0x00002020L,0x04002020L,0x00042020L,0x04042020L, 0x00002022L,0x04002022L,0x00042022L,0x04042022L, 0x00000800L,0x04000800L,0x00040800L,0x04040800L, 0x00000802L,0x04000802L,0x00040802L,0x04040802L, 0x00002800L,0x04002800L,0x00042800L,0x04042800L, 0x00002802L,0x04002802L,0x00042802L,0x04042802L, 0x00000820L,0x04000820L,0x00040820L,0x04040820L, 0x00000822L,0x04000822L,0x00040822L,0x04040822L, 0x00002820L,0x04002820L,0x00042820L,0x04042820L, 0x00002822L,0x04002822L,0x00042822L,0x04042822L, }}; Index: head/sys/crypto/des/spr.h =================================================================== --- head/sys/crypto/des/spr.h (revision 62586) +++ head/sys/crypto/des/spr.h (revision 62587) @@ -1,195 +1,196 @@ +/* $FreeBSD$ */ +/* $KAME: spr.h,v 1.3 2000/03/27 04:36:35 sumikawa Exp $ */ + /* crypto/des/spr.h */ /* Copyright (C) 1995-1996 Eric Young (eay@mincom.oz.au) * All rights reserved. * * This file is part of an SSL implementation written * by Eric Young (eay@mincom.oz.au). * The implementation was written so as to conform with Netscapes SSL * specification. This library and applications are * FREE FOR COMMERCIAL AND NON-COMMERCIAL USE * as long as the following conditions are aheared to. * * Copyright remains Eric Young's, and as such any Copyright notices in * the code are not to be removed. If this code is used in a product, * Eric Young should be given attribution as the author of the parts used. * This can be in the form of a textual message at program startup or * in documentation (online or textual) provided with the package. * * 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 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 Eric Young (eay@mincom.oz.au) * * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * The licence and distribution terms for any publically available version or * derivative of this code cannot be changed. i.e. this code cannot simply be * copied and put under another distribution licence * [including the GNU Public Licence.] - * - * $FreeBSD$ */ static const DES_LONG des_SPtrans[8][64]={ { /* nibble 0 */ 0x00820200L, 0x00020000L, 0x80800000L, 0x80820200L, 0x00800000L, 0x80020200L, 0x80020000L, 0x80800000L, 0x80020200L, 0x00820200L, 0x00820000L, 0x80000200L, 0x80800200L, 0x00800000L, 0x00000000L, 0x80020000L, 0x00020000L, 0x80000000L, 0x00800200L, 0x00020200L, 0x80820200L, 0x00820000L, 0x80000200L, 0x00800200L, 0x80000000L, 0x00000200L, 0x00020200L, 0x80820000L, 0x00000200L, 0x80800200L, 0x80820000L, 0x00000000L, 0x00000000L, 0x80820200L, 0x00800200L, 0x80020000L, 0x00820200L, 0x00020000L, 0x80000200L, 0x00800200L, 0x80820000L, 0x00000200L, 0x00020200L, 0x80800000L, 0x80020200L, 0x80000000L, 0x80800000L, 0x00820000L, 0x80820200L, 0x00020200L, 0x00820000L, 0x80800200L, 0x00800000L, 0x80000200L, 0x80020000L, 0x00000000L, 0x00020000L, 0x00800000L, 0x80800200L, 0x00820200L, 0x80000000L, 0x80820000L, 0x00000200L, 0x80020200L, },{ /* nibble 1 */ 0x10042004L, 0x00000000L, 0x00042000L, 0x10040000L, 0x10000004L, 0x00002004L, 0x10002000L, 0x00042000L, 0x00002000L, 0x10040004L, 0x00000004L, 0x10002000L, 0x00040004L, 0x10042000L, 0x10040000L, 0x00000004L, 0x00040000L, 0x10002004L, 0x10040004L, 0x00002000L, 0x00042004L, 0x10000000L, 0x00000000L, 0x00040004L, 0x10002004L, 0x00042004L, 0x10042000L, 0x10000004L, 0x10000000L, 0x00040000L, 0x00002004L, 0x10042004L, 0x00040004L, 0x10042000L, 0x10002000L, 0x00042004L, 0x10042004L, 0x00040004L, 0x10000004L, 0x00000000L, 0x10000000L, 0x00002004L, 0x00040000L, 0x10040004L, 0x00002000L, 0x10000000L, 0x00042004L, 0x10002004L, 0x10042000L, 0x00002000L, 0x00000000L, 0x10000004L, 0x00000004L, 0x10042004L, 0x00042000L, 0x10040000L, 0x10040004L, 0x00040000L, 0x00002004L, 0x10002000L, 0x10002004L, 0x00000004L, 0x10040000L, 0x00042000L, },{ /* nibble 2 */ 0x41000000L, 0x01010040L, 0x00000040L, 0x41000040L, 0x40010000L, 0x01000000L, 0x41000040L, 0x00010040L, 0x01000040L, 0x00010000L, 0x01010000L, 0x40000000L, 0x41010040L, 0x40000040L, 0x40000000L, 0x41010000L, 0x00000000L, 0x40010000L, 0x01010040L, 0x00000040L, 0x40000040L, 0x41010040L, 0x00010000L, 0x41000000L, 0x41010000L, 0x01000040L, 0x40010040L, 0x01010000L, 0x00010040L, 0x00000000L, 0x01000000L, 0x40010040L, 0x01010040L, 0x00000040L, 0x40000000L, 0x00010000L, 0x40000040L, 0x40010000L, 0x01010000L, 0x41000040L, 0x00000000L, 0x01010040L, 0x00010040L, 0x41010000L, 0x40010000L, 0x01000000L, 0x41010040L, 0x40000000L, 0x40010040L, 0x41000000L, 0x01000000L, 0x41010040L, 0x00010000L, 0x01000040L, 0x41000040L, 0x00010040L, 0x01000040L, 0x00000000L, 0x41010000L, 0x40000040L, 0x41000000L, 0x40010040L, 0x00000040L, 0x01010000L, },{ /* nibble 3 */ 0x00100402L, 0x04000400L, 0x00000002L, 0x04100402L, 0x00000000L, 0x04100000L, 0x04000402L, 0x00100002L, 0x04100400L, 0x04000002L, 0x04000000L, 0x00000402L, 0x04000002L, 0x00100402L, 0x00100000L, 0x04000000L, 0x04100002L, 0x00100400L, 0x00000400L, 0x00000002L, 0x00100400L, 0x04000402L, 0x04100000L, 0x00000400L, 0x00000402L, 0x00000000L, 0x00100002L, 0x04100400L, 0x04000400L, 0x04100002L, 0x04100402L, 0x00100000L, 0x04100002L, 0x00000402L, 0x00100000L, 0x04000002L, 0x00100400L, 0x04000400L, 0x00000002L, 0x04100000L, 0x04000402L, 0x00000000L, 0x00000400L, 0x00100002L, 0x00000000L, 0x04100002L, 0x04100400L, 0x00000400L, 0x04000000L, 0x04100402L, 0x00100402L, 0x00100000L, 0x04100402L, 0x00000002L, 0x04000400L, 0x00100402L, 0x00100002L, 0x00100400L, 0x04100000L, 0x04000402L, 0x00000402L, 0x04000000L, 0x04000002L, 0x04100400L, },{ /* nibble 4 */ 0x02000000L, 0x00004000L, 0x00000100L, 0x02004108L, 0x02004008L, 0x02000100L, 0x00004108L, 0x02004000L, 0x00004000L, 0x00000008L, 0x02000008L, 0x00004100L, 0x02000108L, 0x02004008L, 0x02004100L, 0x00000000L, 0x00004100L, 0x02000000L, 0x00004008L, 0x00000108L, 0x02000100L, 0x00004108L, 0x00000000L, 0x02000008L, 0x00000008L, 0x02000108L, 0x02004108L, 0x00004008L, 0x02004000L, 0x00000100L, 0x00000108L, 0x02004100L, 0x02004100L, 0x02000108L, 0x00004008L, 0x02004000L, 0x00004000L, 0x00000008L, 0x02000008L, 0x02000100L, 0x02000000L, 0x00004100L, 0x02004108L, 0x00000000L, 0x00004108L, 0x02000000L, 0x00000100L, 0x00004008L, 0x02000108L, 0x00000100L, 0x00000000L, 0x02004108L, 0x02004008L, 0x02004100L, 0x00000108L, 0x00004000L, 0x00004100L, 0x02004008L, 0x02000100L, 0x00000108L, 0x00000008L, 0x00004108L, 0x02004000L, 0x02000008L, },{ /* nibble 5 */ 0x20000010L, 0x00080010L, 0x00000000L, 0x20080800L, 0x00080010L, 0x00000800L, 0x20000810L, 0x00080000L, 0x00000810L, 0x20080810L, 0x00080800L, 0x20000000L, 0x20000800L, 0x20000010L, 0x20080000L, 0x00080810L, 0x00080000L, 0x20000810L, 0x20080010L, 0x00000000L, 0x00000800L, 0x00000010L, 0x20080800L, 0x20080010L, 0x20080810L, 0x20080000L, 0x20000000L, 0x00000810L, 0x00000010L, 0x00080800L, 0x00080810L, 0x20000800L, 0x00000810L, 0x20000000L, 0x20000800L, 0x00080810L, 0x20080800L, 0x00080010L, 0x00000000L, 0x20000800L, 0x20000000L, 0x00000800L, 0x20080010L, 0x00080000L, 0x00080010L, 0x20080810L, 0x00080800L, 0x00000010L, 0x20080810L, 0x00080800L, 0x00080000L, 0x20000810L, 0x20000010L, 0x20080000L, 0x00080810L, 0x00000000L, 0x00000800L, 0x20000010L, 0x20000810L, 0x20080800L, 0x20080000L, 0x00000810L, 0x00000010L, 0x20080010L, },{ /* nibble 6 */ 0x00001000L, 0x00000080L, 0x00400080L, 0x00400001L, 0x00401081L, 0x00001001L, 0x00001080L, 0x00000000L, 0x00400000L, 0x00400081L, 0x00000081L, 0x00401000L, 0x00000001L, 0x00401080L, 0x00401000L, 0x00000081L, 0x00400081L, 0x00001000L, 0x00001001L, 0x00401081L, 0x00000000L, 0x00400080L, 0x00400001L, 0x00001080L, 0x00401001L, 0x00001081L, 0x00401080L, 0x00000001L, 0x00001081L, 0x00401001L, 0x00000080L, 0x00400000L, 0x00001081L, 0x00401000L, 0x00401001L, 0x00000081L, 0x00001000L, 0x00000080L, 0x00400000L, 0x00401001L, 0x00400081L, 0x00001081L, 0x00001080L, 0x00000000L, 0x00000080L, 0x00400001L, 0x00000001L, 0x00400080L, 0x00000000L, 0x00400081L, 0x00400080L, 0x00001080L, 0x00000081L, 0x00001000L, 0x00401081L, 0x00400000L, 0x00401080L, 0x00000001L, 0x00001001L, 0x00401081L, 0x00400001L, 0x00401080L, 0x00401000L, 0x00001001L, },{ /* nibble 7 */ 0x08200020L, 0x08208000L, 0x00008020L, 0x00000000L, 0x08008000L, 0x00200020L, 0x08200000L, 0x08208020L, 0x00000020L, 0x08000000L, 0x00208000L, 0x00008020L, 0x00208020L, 0x08008020L, 0x08000020L, 0x08200000L, 0x00008000L, 0x00208020L, 0x00200020L, 0x08008000L, 0x08208020L, 0x08000020L, 0x00000000L, 0x00208000L, 0x08000000L, 0x00200000L, 0x08008020L, 0x08200020L, 0x00200000L, 0x00008000L, 0x08208000L, 0x00000020L, 0x00200000L, 0x00008000L, 0x08000020L, 0x08208020L, 0x00008020L, 0x08000000L, 0x00000000L, 0x00208000L, 0x08200020L, 0x08008020L, 0x08008000L, 0x00200020L, 0x08208000L, 0x00000020L, 0x00200020L, 0x08008000L, 0x08208020L, 0x00200000L, 0x08200000L, 0x08000020L, 0x00208000L, 0x00008020L, 0x08008020L, 0x08200000L, 0x00000020L, 0x08208000L, 0x00208020L, 0x00000000L, 0x08000000L, 0x08200020L, 0x00008000L, 0x00208020L, }}; Index: head/sys/crypto/md5.c =================================================================== --- head/sys/crypto/md5.c (revision 62586) +++ head/sys/crypto/md5.c (revision 62587) @@ -1,307 +1,308 @@ +/* $FreeBSD$ */ +/* $KAME: md5.c,v 1.4 2000/03/27 04:36:22 sumikawa Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ #include #include #include #include #include -#define SHIFT(X, s) (((X) << (s)) | ((X) >> (32 - (s)))) +#define SHIFT(X, s) (((X) << (s)) | ((X) >> (32 - (s)))) -#define F(X, Y, Z) (((X) & (Y)) | ((~X) & (Z))) -#define G(X, Y, Z) (((X) & (Z)) | ((Y) & (~Z))) -#define H(X, Y, Z) ((X) ^ (Y) ^ (Z)) -#define I(X, Y, Z) ((Y) ^ ((X) | (~Z))) +#define F(X, Y, Z) (((X) & (Y)) | ((~X) & (Z))) +#define G(X, Y, Z) (((X) & (Z)) | ((Y) & (~Z))) +#define H(X, Y, Z) ((X) ^ (Y) ^ (Z)) +#define I(X, Y, Z) ((Y) ^ ((X) | (~Z))) -#define ROUND1(a, b, c, d, k, s, i) { \ +#define ROUND1(a, b, c, d, k, s, i) { \ (a) = (a) + F((b), (c), (d)) + X[(k)] + T[(i)]; \ (a) = SHIFT((a), (s)); \ (a) = (b) + (a); \ } -#define ROUND2(a, b, c, d, k, s, i) { \ +#define ROUND2(a, b, c, d, k, s, i) { \ (a) = (a) + G((b), (c), (d)) + X[(k)] + T[(i)]; \ (a) = SHIFT((a), (s)); \ (a) = (b) + (a); \ } -#define ROUND3(a, b, c, d, k, s, i) { \ +#define ROUND3(a, b, c, d, k, s, i) { \ (a) = (a) + H((b), (c), (d)) + X[(k)] + T[(i)]; \ (a) = SHIFT((a), (s)); \ (a) = (b) + (a); \ } -#define ROUND4(a, b, c, d, k, s, i) { \ +#define ROUND4(a, b, c, d, k, s, i) { \ (a) = (a) + I((b), (c), (d)) + X[(k)] + T[(i)]; \ (a) = SHIFT((a), (s)); \ (a) = (b) + (a); \ } -#define Sa 7 -#define Sb 12 -#define Sc 17 -#define Sd 22 +#define Sa 7 +#define Sb 12 +#define Sc 17 +#define Sd 22 -#define Se 5 -#define Sf 9 -#define Sg 14 -#define Sh 20 +#define Se 5 +#define Sf 9 +#define Sg 14 +#define Sh 20 -#define Si 4 -#define Sj 11 -#define Sk 16 -#define Sl 23 +#define Si 4 +#define Sj 11 +#define Sk 16 +#define Sl 23 -#define Sm 6 -#define Sn 10 -#define So 15 -#define Sp 21 +#define Sm 6 +#define Sn 10 +#define So 15 +#define Sp 21 -#define MD5_A0 0x67452301 -#define MD5_B0 0xefcdab89 -#define MD5_C0 0x98badcfe -#define MD5_D0 0x10325476 +#define MD5_A0 0x67452301 +#define MD5_B0 0xefcdab89 +#define MD5_C0 0x98badcfe +#define MD5_D0 0x10325476 /* Integer part of 4294967296 times abs(sin(i)), where i is in radians. */ static const u_int32_t T[65] = { 0, 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x2441453, 0xd8a1e681, 0xe7d3fbc8, 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x4881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391, }; static const u_int8_t md5_paddat[MD5_BUFLEN] = { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, }; static void md5_calc __P((u_int8_t *, md5_ctxt *)); void md5_init(ctxt) md5_ctxt *ctxt; { ctxt->md5_n = 0; ctxt->md5_i = 0; ctxt->md5_sta = MD5_A0; ctxt->md5_stb = MD5_B0; ctxt->md5_stc = MD5_C0; ctxt->md5_std = MD5_D0; bzero(ctxt->md5_buf, sizeof(ctxt->md5_buf)); } void md5_loop(ctxt, input, len) md5_ctxt *ctxt; u_int8_t *input; u_int len; /* number of bytes */ { u_int gap, i; ctxt->md5_n += len * 8; /* byte to bit */ gap = MD5_BUFLEN - ctxt->md5_i; if (len >= gap) { bcopy((void *)input, (void *)(ctxt->md5_buf + ctxt->md5_i), gap); md5_calc(ctxt->md5_buf, ctxt); for (i = gap; i + MD5_BUFLEN <= len; i += MD5_BUFLEN) { md5_calc((u_int8_t *)(input + i), ctxt); } - + ctxt->md5_i = len - i; bcopy((void *)(input + i), (void *)ctxt->md5_buf, ctxt->md5_i); } else { bcopy((void *)input, (void *)(ctxt->md5_buf + ctxt->md5_i), len); ctxt->md5_i += len; } } void md5_pad(ctxt) md5_ctxt *ctxt; { u_int gap; - /* Don't count up padding. Keep md5_n. */ + /* Don't count up padding. Keep md5_n. */ gap = MD5_BUFLEN - ctxt->md5_i; if (gap > 8) { bcopy((void *)md5_paddat, (void *)(ctxt->md5_buf + ctxt->md5_i), gap - sizeof(ctxt->md5_n)); } else { /* including gap == 8 */ bcopy((void *)md5_paddat, (void *)(ctxt->md5_buf + ctxt->md5_i), gap); md5_calc(ctxt->md5_buf, ctxt); bcopy((void *)(md5_paddat + gap), (void *)ctxt->md5_buf, MD5_BUFLEN - sizeof(ctxt->md5_n)); } - /* 8 byte word */ + /* 8 byte word */ #if BYTE_ORDER == LITTLE_ENDIAN bcopy(&ctxt->md5_n8[0], &ctxt->md5_buf[56], 8); #endif #if BYTE_ORDER == BIG_ENDIAN ctxt->md5_buf[56] = ctxt->md5_n8[7]; ctxt->md5_buf[57] = ctxt->md5_n8[6]; ctxt->md5_buf[58] = ctxt->md5_n8[5]; ctxt->md5_buf[59] = ctxt->md5_n8[4]; ctxt->md5_buf[60] = ctxt->md5_n8[3]; ctxt->md5_buf[61] = ctxt->md5_n8[2]; ctxt->md5_buf[62] = ctxt->md5_n8[1]; ctxt->md5_buf[63] = ctxt->md5_n8[0]; #endif md5_calc(ctxt->md5_buf, ctxt); } void md5_result(digest, ctxt) u_int8_t *digest; md5_ctxt *ctxt; { /* 4 byte words */ #if BYTE_ORDER == LITTLE_ENDIAN bcopy(&ctxt->md5_st8[0], digest, 16); #endif #if BYTE_ORDER == BIG_ENDIAN digest[ 0] = ctxt->md5_st8[ 3]; digest[ 1] = ctxt->md5_st8[ 2]; digest[ 2] = ctxt->md5_st8[ 1]; digest[ 3] = ctxt->md5_st8[ 0]; digest[ 4] = ctxt->md5_st8[ 7]; digest[ 5] = ctxt->md5_st8[ 6]; digest[ 6] = ctxt->md5_st8[ 5]; digest[ 7] = ctxt->md5_st8[ 4]; digest[ 8] = ctxt->md5_st8[11]; digest[ 9] = ctxt->md5_st8[10]; digest[10] = ctxt->md5_st8[ 9]; digest[11] = ctxt->md5_st8[ 8]; digest[12] = ctxt->md5_st8[15]; digest[13] = ctxt->md5_st8[14]; digest[14] = ctxt->md5_st8[13]; digest[15] = ctxt->md5_st8[12]; #endif } #if BYTE_ORDER == BIG_ENDIAN u_int32_t X[16]; #endif static void md5_calc(b64, ctxt) u_int8_t *b64; md5_ctxt *ctxt; { u_int32_t A = ctxt->md5_sta; u_int32_t B = ctxt->md5_stb; u_int32_t C = ctxt->md5_stc; u_int32_t D = ctxt->md5_std; #if BYTE_ORDER == LITTLE_ENDIAN u_int32_t *X = (u_int32_t *)b64; -#endif +#endif #if BYTE_ORDER == BIG_ENDIAN /* 4 byte words */ /* what a brute force but fast! */ u_int8_t *y = (u_int8_t *)X; y[ 0] = b64[ 3]; y[ 1] = b64[ 2]; y[ 2] = b64[ 1]; y[ 3] = b64[ 0]; y[ 4] = b64[ 7]; y[ 5] = b64[ 6]; y[ 6] = b64[ 5]; y[ 7] = b64[ 4]; y[ 8] = b64[11]; y[ 9] = b64[10]; y[10] = b64[ 9]; y[11] = b64[ 8]; y[12] = b64[15]; y[13] = b64[14]; y[14] = b64[13]; y[15] = b64[12]; y[16] = b64[19]; y[17] = b64[18]; y[18] = b64[17]; y[19] = b64[16]; y[20] = b64[23]; y[21] = b64[22]; y[22] = b64[21]; y[23] = b64[20]; y[24] = b64[27]; y[25] = b64[26]; y[26] = b64[25]; y[27] = b64[24]; y[28] = b64[31]; y[29] = b64[30]; y[30] = b64[29]; y[31] = b64[28]; y[32] = b64[35]; y[33] = b64[34]; y[34] = b64[33]; y[35] = b64[32]; y[36] = b64[39]; y[37] = b64[38]; y[38] = b64[37]; y[39] = b64[36]; y[40] = b64[43]; y[41] = b64[42]; y[42] = b64[41]; y[43] = b64[40]; y[44] = b64[47]; y[45] = b64[46]; y[46] = b64[45]; y[47] = b64[44]; y[48] = b64[51]; y[49] = b64[50]; y[50] = b64[49]; y[51] = b64[48]; y[52] = b64[55]; y[53] = b64[54]; y[54] = b64[53]; y[55] = b64[52]; y[56] = b64[59]; y[57] = b64[58]; y[58] = b64[57]; y[59] = b64[56]; y[60] = b64[63]; y[61] = b64[62]; y[62] = b64[61]; y[63] = b64[60]; #endif ROUND1(A, B, C, D, 0, Sa, 1); ROUND1(D, A, B, C, 1, Sb, 2); ROUND1(C, D, A, B, 2, Sc, 3); ROUND1(B, C, D, A, 3, Sd, 4); ROUND1(A, B, C, D, 4, Sa, 5); ROUND1(D, A, B, C, 5, Sb, 6); ROUND1(C, D, A, B, 6, Sc, 7); ROUND1(B, C, D, A, 7, Sd, 8); ROUND1(A, B, C, D, 8, Sa, 9); ROUND1(D, A, B, C, 9, Sb, 10); ROUND1(C, D, A, B, 10, Sc, 11); ROUND1(B, C, D, A, 11, Sd, 12); ROUND1(A, B, C, D, 12, Sa, 13); ROUND1(D, A, B, C, 13, Sb, 14); ROUND1(C, D, A, B, 14, Sc, 15); ROUND1(B, C, D, A, 15, Sd, 16); - + ROUND2(A, B, C, D, 1, Se, 17); ROUND2(D, A, B, C, 6, Sf, 18); ROUND2(C, D, A, B, 11, Sg, 19); ROUND2(B, C, D, A, 0, Sh, 20); ROUND2(A, B, C, D, 5, Se, 21); ROUND2(D, A, B, C, 10, Sf, 22); ROUND2(C, D, A, B, 15, Sg, 23); ROUND2(B, C, D, A, 4, Sh, 24); ROUND2(A, B, C, D, 9, Se, 25); ROUND2(D, A, B, C, 14, Sf, 26); ROUND2(C, D, A, B, 3, Sg, 27); ROUND2(B, C, D, A, 8, Sh, 28); ROUND2(A, B, C, D, 13, Se, 29); ROUND2(D, A, B, C, 2, Sf, 30); ROUND2(C, D, A, B, 7, Sg, 31); ROUND2(B, C, D, A, 12, Sh, 32); ROUND3(A, B, C, D, 5, Si, 33); ROUND3(D, A, B, C, 8, Sj, 34); ROUND3(C, D, A, B, 11, Sk, 35); ROUND3(B, C, D, A, 14, Sl, 36); ROUND3(A, B, C, D, 1, Si, 37); ROUND3(D, A, B, C, 4, Sj, 38); ROUND3(C, D, A, B, 7, Sk, 39); ROUND3(B, C, D, A, 10, Sl, 40); ROUND3(A, B, C, D, 13, Si, 41); ROUND3(D, A, B, C, 0, Sj, 42); ROUND3(C, D, A, B, 3, Sk, 43); ROUND3(B, C, D, A, 6, Sl, 44); ROUND3(A, B, C, D, 9, Si, 45); ROUND3(D, A, B, C, 12, Sj, 46); ROUND3(C, D, A, B, 15, Sk, 47); ROUND3(B, C, D, A, 2, Sl, 48); - - ROUND4(A, B, C, D, 0, Sm, 49); ROUND4(D, A, B, C, 7, Sn, 50); - ROUND4(C, D, A, B, 14, So, 51); ROUND4(B, C, D, A, 5, Sp, 52); - ROUND4(A, B, C, D, 12, Sm, 53); ROUND4(D, A, B, C, 3, Sn, 54); - ROUND4(C, D, A, B, 10, So, 55); ROUND4(B, C, D, A, 1, Sp, 56); - ROUND4(A, B, C, D, 8, Sm, 57); ROUND4(D, A, B, C, 15, Sn, 58); - ROUND4(C, D, A, B, 6, So, 59); ROUND4(B, C, D, A, 13, Sp, 60); - ROUND4(A, B, C, D, 4, Sm, 61); ROUND4(D, A, B, C, 11, Sn, 62); + + ROUND4(A, B, C, D, 0, Sm, 49); ROUND4(D, A, B, C, 7, Sn, 50); + ROUND4(C, D, A, B, 14, So, 51); ROUND4(B, C, D, A, 5, Sp, 52); + ROUND4(A, B, C, D, 12, Sm, 53); ROUND4(D, A, B, C, 3, Sn, 54); + ROUND4(C, D, A, B, 10, So, 55); ROUND4(B, C, D, A, 1, Sp, 56); + ROUND4(A, B, C, D, 8, Sm, 57); ROUND4(D, A, B, C, 15, Sn, 58); + ROUND4(C, D, A, B, 6, So, 59); ROUND4(B, C, D, A, 13, Sp, 60); + ROUND4(A, B, C, D, 4, Sm, 61); ROUND4(D, A, B, C, 11, Sn, 62); ROUND4(C, D, A, B, 2, So, 63); ROUND4(B, C, D, A, 9, Sp, 64); ctxt->md5_sta += A; ctxt->md5_stb += B; ctxt->md5_stc += C; ctxt->md5_std += D; } Index: head/sys/crypto/md5.h =================================================================== --- head/sys/crypto/md5.h (revision 62586) +++ head/sys/crypto/md5.h (revision 62587) @@ -1,75 +1,76 @@ +/* $FreeBSD$ */ +/* $KAME: md5.h,v 1.4 2000/03/27 04:36:22 sumikawa Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ #ifndef _NETINET6_MD5_H_ -#define _NETINET6_MD5_H_ +#define _NETINET6_MD5_H_ -#define MD5_BUFLEN 64 +#define MD5_BUFLEN 64 typedef struct { union { u_int32_t md5_state32[4]; u_int8_t md5_state8[16]; } md5_st; -#define md5_sta md5_st.md5_state32[0] -#define md5_stb md5_st.md5_state32[1] -#define md5_stc md5_st.md5_state32[2] -#define md5_std md5_st.md5_state32[3] -#define md5_st8 md5_st.md5_state8 +#define md5_sta md5_st.md5_state32[0] +#define md5_stb md5_st.md5_state32[1] +#define md5_stc md5_st.md5_state32[2] +#define md5_std md5_st.md5_state32[3] +#define md5_st8 md5_st.md5_state8 union { u_int64_t md5_count64; u_int8_t md5_count8[8]; } md5_count; -#define md5_n md5_count.md5_count64 -#define md5_n8 md5_count.md5_count8 +#define md5_n md5_count.md5_count64 +#define md5_n8 md5_count.md5_count8 u_int md5_i; u_int8_t md5_buf[MD5_BUFLEN]; } md5_ctxt; extern void md5_init __P((md5_ctxt *)); extern void md5_loop __P((md5_ctxt *, u_int8_t *, u_int)); extern void md5_pad __P((md5_ctxt *)); extern void md5_result __P((u_int8_t *, md5_ctxt *)); /* compatibility */ -#define MD5_CTX md5_ctxt -#define MD5Init(x) md5_init((x)) -#define MD5Update(x, y, z) md5_loop((x), (y), (z)) -#define MD5Final(x, y) \ +#define MD5_CTX md5_ctxt +#define MD5Init(x) md5_init((x)) +#define MD5Update(x, y, z) md5_loop((x), (y), (z)) +#define MD5Final(x, y) \ do { \ md5_pad((y)); \ md5_result((x), (y)); \ } while (0) #endif /* ! _NETINET6_MD5_H_*/ Index: head/sys/crypto/rc5/rc5.c =================================================================== --- head/sys/crypto/rc5/rc5.c (revision 62586) +++ head/sys/crypto/rc5/rc5.c (revision 62587) @@ -1,218 +1,219 @@ +/* $FreeBSD$ */ +/* $KAME: rc5.c,v 1.3 2000/03/27 04:36:36 sumikawa Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ #include void set_rc5_expandkey(e_key, key, keylen, rounds) RC5_WORD *e_key; u_int8_t *key; size_t keylen; int rounds; { int i, j, k, LL, t, T; RC5_WORD L[256/WW]; RC5_WORD A, B; LL = (keylen + WW - 1) / WW; bzero(L, sizeof(RC5_WORD)*LL); for (i = 0; i < keylen; i++) { t = (key[i] & 0xff) << (8*(i%4)); L[i/WW] = L[i/WW] + t; } T = 2 * (rounds + 1); e_key[0] = Pw; for (i = 1; i < T; i++) e_key[i] = e_key[i-1] + Qw; i = j = 0; A = B = 0; if (LL > T) k = 3 * LL; else k = 3 * T; for (; k > 0; k--) { A = ROTL(e_key[i]+A+B, 3, W); e_key[i] = A; B = ROTL(L[j]+A+B, A+B, W); L[j] = B; i = (i + 1) % T; j = (j + 1) % LL; } } /* * */ void rc5_encrypt_round16(out, in, e_key) u_int8_t *out; const u_int8_t *in; const RC5_WORD *e_key; { RC5_WORD A, B; const RC5_WORD *e_keyA, *e_keyB; A = in[0] & 0xff; A += (in[1] & 0xff) << 8; A += (in[2] & 0xff) << 16; A += (in[3] & 0xff) << 24; B = in[4] & 0xff; B += (in[5] & 0xff) << 8; B += (in[6] & 0xff) << 16; B += (in[7] & 0xff) << 24; e_keyA = e_key; e_keyB = e_key + 1; A += *e_keyA; e_keyA += 2; B += *e_keyB; e_keyB += 2; A = ROTL(A^B, B, W) + *e_keyA; e_keyA += 2; B = ROTL(B^A, A, W) + *e_keyB; e_keyB += 2; A = ROTL(A^B, B, W) + *e_keyA; e_keyA += 2; B = ROTL(B^A, A, W) + *e_keyB; e_keyB += 2; A = ROTL(A^B, B, W) + *e_keyA; e_keyA += 2; B = ROTL(B^A, A, W) + *e_keyB; e_keyB += 2; A = ROTL(A^B, B, W) + *e_keyA; e_keyA += 2; B = ROTL(B^A, A, W) + *e_keyB; e_keyB += 2; /* round 4 */ A = ROTL(A^B, B, W) + *e_keyA; e_keyA += 2; B = ROTL(B^A, A, W) + *e_keyB; e_keyB += 2; A = ROTL(A^B, B, W) + *e_keyA; e_keyA += 2; B = ROTL(B^A, A, W) + *e_keyB; e_keyB += 2; A = ROTL(A^B, B, W) + *e_keyA; e_keyA += 2; B = ROTL(B^A, A, W) + *e_keyB; e_keyB += 2; A = ROTL(A^B, B, W) + *e_keyA; e_keyA += 2; B = ROTL(B^A, A, W) + *e_keyB; e_keyB += 2; /* round 8 */ A = ROTL(A^B, B, W) + *e_keyA; e_keyA += 2; B = ROTL(B^A, A, W) + *e_keyB; e_keyB += 2; A = ROTL(A^B, B, W) + *e_keyA; e_keyA += 2; B = ROTL(B^A, A, W) + *e_keyB; e_keyB += 2; A = ROTL(A^B, B, W) + *e_keyA; e_keyA += 2; B = ROTL(B^A, A, W) + *e_keyB; e_keyB += 2; A = ROTL(A^B, B, W) + *e_keyA; e_keyA += 2; B = ROTL(B^A, A, W) + *e_keyB; e_keyB += 2; /* round 12 */ A = ROTL(A^B, B, W) + *e_keyA; e_keyA += 2; B = ROTL(B^A, A, W) + *e_keyB; e_keyB += 2; A = ROTL(A^B, B, W) + *e_keyA; e_keyA += 2; B = ROTL(B^A, A, W) + *e_keyB; e_keyB += 2; A = ROTL(A^B, B, W) + *e_keyA; e_keyA += 2; B = ROTL(B^A, A, W) + *e_keyB; e_keyB += 2; A = ROTL(A^B, B, W) + *e_keyA; e_keyA += 2; B = ROTL(B^A, A, W) + *e_keyB; e_keyB += 2; /* round 16 */ out[0] = A & 0xff; out[1] = (A >> 8) & 0xff; out[2] = (A >> 16) & 0xff; out[3] = (A >> 24) & 0xff; out[4] = B & 0xff; out[5] = (B >> 8) & 0xff; out[6] = (B >> 16) & 0xff; out[7] = (B >> 24) & 0xff; } /* * */ void rc5_decrypt_round16(out, in, e_key) u_int8_t *out; const u_int8_t *in; const RC5_WORD *e_key; { RC5_WORD A, B; const RC5_WORD *e_keyA, *e_keyB; A = in[0] & 0xff; A += (in[1] & 0xff) << 8; A += (in[2] & 0xff) << 16; A += (in[3] & 0xff) << 24; B = in[4] & 0xff; B += (in[5] & 0xff) << 8; B += (in[6] & 0xff) << 16; B += (in[7] & 0xff) << 24; e_keyA = e_key + 2*16; e_keyB = e_key + 2*16 + 1; B = ROTR(B-*e_keyB, A, W) ^ A; e_keyB -= 2; A = ROTR(A-*e_keyA, B, W) ^ B; e_keyA -= 2; B = ROTR(B-*e_keyB, A, W) ^ A; e_keyB -= 2; A = ROTR(A-*e_keyA, B, W) ^ B; e_keyA -= 2; B = ROTR(B-*e_keyB, A, W) ^ A; e_keyB -= 2; A = ROTR(A-*e_keyA, B, W) ^ B; e_keyA -= 2; B = ROTR(B-*e_keyB, A, W) ^ A; e_keyB -= 2; A = ROTR(A-*e_keyA, B, W) ^ B; e_keyA -= 2; /* round 4 */ B = ROTR(B-*e_keyB, A, W) ^ A; e_keyB -= 2; A = ROTR(A-*e_keyA, B, W) ^ B; e_keyA -= 2; B = ROTR(B-*e_keyB, A, W) ^ A; e_keyB -= 2; A = ROTR(A-*e_keyA, B, W) ^ B; e_keyA -= 2; B = ROTR(B-*e_keyB, A, W) ^ A; e_keyB -= 2; A = ROTR(A-*e_keyA, B, W) ^ B; e_keyA -= 2; B = ROTR(B-*e_keyB, A, W) ^ A; e_keyB -= 2; A = ROTR(A-*e_keyA, B, W) ^ B; e_keyA -= 2; /* round 8 */ B = ROTR(B-*e_keyB, A, W) ^ A; e_keyB -= 2; A = ROTR(A-*e_keyA, B, W) ^ B; e_keyA -= 2; B = ROTR(B-*e_keyB, A, W) ^ A; e_keyB -= 2; A = ROTR(A-*e_keyA, B, W) ^ B; e_keyA -= 2; B = ROTR(B-*e_keyB, A, W) ^ A; e_keyB -= 2; A = ROTR(A-*e_keyA, B, W) ^ B; e_keyA -= 2; B = ROTR(B-*e_keyB, A, W) ^ A; e_keyB -= 2; A = ROTR(A-*e_keyA, B, W) ^ B; e_keyA -= 2; /* round 12 */ B = ROTR(B-*e_keyB, A, W) ^ A; e_keyB -= 2; A = ROTR(A-*e_keyA, B, W) ^ B; e_keyA -= 2; B = ROTR(B-*e_keyB, A, W) ^ A; e_keyB -= 2; A = ROTR(A-*e_keyA, B, W) ^ B; e_keyA -= 2; B = ROTR(B-*e_keyB, A, W) ^ A; e_keyB -= 2; A = ROTR(A-*e_keyA, B, W) ^ B; e_keyA -= 2; B = ROTR(B-*e_keyB, A, W) ^ A; e_keyB -= 2; A = ROTR(A-*e_keyA, B, W) ^ B; e_keyA -= 2; /* round 16 */ B = B - *e_keyB; A = A - *e_keyA; out[0] = A & 0xff; out[1] = (A >> 8) & 0xff; out[2] = (A >> 16) & 0xff; out[3] = (A >> 24) & 0xff; out[4] = B & 0xff; out[5] = (B >> 8) & 0xff; out[6] = (B >> 16) & 0xff; out[7] = (B >> 24) & 0xff; } Index: head/sys/crypto/rc5/rc5.h =================================================================== --- head/sys/crypto/rc5/rc5.h (revision 62586) +++ head/sys/crypto/rc5/rc5.h (revision 62587) @@ -1,86 +1,87 @@ +/* $FreeBSD$ */ +/* $KAME: rc5.h,v 1.4 2000/06/14 10:41:17 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ #ifndef _RFC2040_RC5_H_ -#define _RFC2040_RC5_H_ +#define _RFC2040_RC5_H_ #include #include #include /* * if RC5_WORD change, W also may be changed. */ typedef u_int32_t RC5_WORD; -#define W (32) -#define WW (W / 8) -#define ROT_MASK (W - 1) -#define BB ((2 * W) / 8) +#define W (32) +#define WW (W / 8) +#define ROT_MASK (W - 1) +#define BB ((2 * W) / 8) -#define SHLL(x, s) ((RC5_WORD)((x) << ((s)&ROT_MASK))) -#define SHLR(x, s, w) ((RC5_WORD)((x) >> ((w)-((s)&ROT_MASK)))) -#define SHRL(x, s, w) ((RC5_WORD)((x) << ((w)-((s)&ROT_MASK)))) -#define SHRR(x, s) ((RC5_WORD)((x) >> ((s)&ROT_MASK))) +#define SHLL(x, s) ((RC5_WORD)((x) << ((s)&ROT_MASK))) +#define SHLR(x, s, w) ((RC5_WORD)((x) >> ((w)-((s)&ROT_MASK)))) +#define SHRL(x, s, w) ((RC5_WORD)((x) << ((w)-((s)&ROT_MASK)))) +#define SHRR(x, s) ((RC5_WORD)((x) >> ((s)&ROT_MASK))) -#define ROTL(x, s, w) ((RC5_WORD)(SHLL((x), (s))|SHLR((x), (s), (w)))) -#define ROTR(x, s, w) ((RC5_WORD)(SHRL((x), (s), (w))|SHRR((x), (s)))) +#define ROTL(x, s, w) ((RC5_WORD)(SHLL((x), (s))|SHLR((x), (s), (w)))) +#define ROTR(x, s, w) ((RC5_WORD)(SHRL((x), (s), (w))|SHRR((x), (s)))) -#define P16 0xb7e1 -#define Q16 0x9e37 -#define P32 0xb7e15163 -#define Q32 0x9e3779b9 -#define P64 0xb7e151628aed2a6b -#define Q64 0x9e3779b97f4a7c15 +#define P16 0xb7e1 +#define Q16 0x9e37 +#define P32 0xb7e15163 +#define Q32 0x9e3779b9 +#define P64 0xb7e151628aed2a6b +#define Q64 0x9e3779b97f4a7c15 #if W == 16 -#define Pw P16 -#define Qw Q16 +#define Pw P16 +#define Qw Q16 #elif W == 32 -#define Pw P32 -#define Qw Q32 +#define Pw P32 +#define Qw Q32 #elif W == 64 -#define Pw P64 -#define Qw Q64 +#define Pw P64 +#define Qw Q64 #endif -#define RC5_ENCRYPT 1 -#define RC5_DECRYPT 0 +#define RC5_ENCRYPT 1 +#define RC5_DECRYPT 0 extern void set_rc5_expandkey __P((RC5_WORD *, u_int8_t *, size_t, int)); extern void rc5_encrypt_round16 __P((u_int8_t *, const u_int8_t *, const RC5_WORD *)); extern void rc5_decrypt_round16 __P((u_int8_t *, const u_int8_t *, const RC5_WORD *)); -extern void rc5_cbc_process __P((struct mbuf *, size_t, size_t, RC5_WORD *, +extern int rc5_cbc_process __P((struct mbuf *, size_t, size_t, RC5_WORD *, u_int8_t *, int)); #endif Index: head/sys/crypto/rc5/rc5_cbc.c =================================================================== --- head/sys/crypto/rc5/rc5_cbc.c (revision 62586) +++ head/sys/crypto/rc5/rc5_cbc.c (revision 62587) @@ -1,211 +1,215 @@ +/* $FreeBSD$ */ +/* $KAME: rc5_cbc.c,v 1.4 2000/06/14 10:41:17 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* * based on sys/crypto/des/des_cbc.c, rewrote by Tomomi Suzuki */ #include +#define panic(x) do { printf(x); return EINVAL; } while (0) -void +int rc5_cbc_process(m0, skip, length, e_key, iv, mode) struct mbuf *m0; size_t skip; size_t length; RC5_WORD *e_key; u_int8_t *iv; int mode; { u_int8_t inbuf[8], outbuf[8]; struct mbuf *m; size_t off; /* sanity check */ if (m0->m_pkthdr.len < skip) { printf("rc5_cbc_process: mbuf length < skip\n"); - return; + return EINVAL; } if (m0->m_pkthdr.len < length) { printf("rc5_cbc_process: mbuf length < encrypt length\n"); - return; + return EINVAL; } if (m0->m_pkthdr.len < skip + length) { printf("rc5_cbc_process: mbuf length < " "skip + encrypt length\n"); - return; + return EINVAL; } if (length % 8) { printf("rc5_cbc_process: length(%lu)is not multipleof 8\n", (u_long)length); - return; + return EINVAL; } m = m0; off = 0; /* skip over the header */ while (skip) { if (!m) panic("rc5_cbc_process: mbuf chain?\n"); if (m->m_len <= skip) { skip -= m->m_len; m = m->m_next; off = 0; } else { off = skip; skip = 0; } } /* copy iv into outbuf for XOR (encrypt) */ bcopy(iv, outbuf, 8); /* * encrypt/decrypt packet */ while (length > 0) { int i; if (!m) panic("rc5_cbc_process: mbuf chain?\n"); /* * copy the source into input buffer. * don't update off or m, since we need to use them * later. */ if (off + 8 <= m->m_len) bcopy(mtod(m, u_int8_t *) + off, &inbuf[0], 8); else { struct mbuf *n; size_t noff; u_int8_t *p; u_int8_t *in; n = m; noff = off; p = mtod(n, u_int8_t *) + noff; in = &inbuf[0]; while (in - &inbuf[0] < 8) { if (!p) { panic("rc5_cbc_process: " "mbuf chain?\n"); } *in++ = *p++; noff++; if (noff < n->m_len) continue; do { n = n->m_next; } while (n && !n->m_len); noff = 0; if (n) p = mtod(n, u_int8_t *) + noff; else p = NULL; } } /* encrypt/decrypt */ switch (mode) { case RC5_ENCRYPT: /* XOR */ for (i = 0; i < 8; i++) inbuf[i] ^= outbuf[i]; /* encrypt */ rc5_encrypt_round16(outbuf, inbuf, e_key); break; case RC5_DECRYPT: /* decrypt */ rc5_decrypt_round16(outbuf, inbuf, e_key); /* XOR */ for (i = 0; i < 8; i++) outbuf[i] ^= iv[i]; /* copy inbuf into iv for next XOR */ bcopy(inbuf, iv, 8); break; } /* * copy the output buffer into the result. * need to update off and m. */ if (off + 8 < m->m_len) { bcopy(&outbuf[0], mtod(m, u_int8_t *) + off, 8); off += 8; } else if (off + 8 == m->m_len) { bcopy(&outbuf[0], mtod(m, u_int8_t *) + off, 8); do { m = m->m_next; } while (m && !m->m_len); off = 0; } else { struct mbuf *n; size_t noff; u_int8_t *p; u_int8_t *out; n = m; noff = off; p = mtod(n, u_int8_t *) + noff; out = &outbuf[0]; while (out - &outbuf[0] < 8) { if (!p) { panic("rc5_cbc_process: " "mbuf chain?\n"); } *p++ = *out++; noff++; if (noff < n->m_len) continue; do { n = n->m_next; } while (n && !n->m_len); noff = 0; if (n) p = mtod(n, u_int8_t *) + noff; else p = NULL; } m = n; off = noff; } length -= 8; } + + return 0; } Index: head/sys/crypto/sha1.c =================================================================== --- head/sys/crypto/sha1.c (revision 62586) +++ head/sys/crypto/sha1.c (revision 62587) @@ -1,273 +1,276 @@ +/* $FreeBSD$ */ +/* $KAME: sha1.c,v 1.4 2000/03/27 04:36:23 sumikawa Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* * FIPS pub 180-1: Secure Hash Algorithm (SHA-1) * based on: http://csrc.nist.gov/fips/fip180-1.txt * implemented by Jun-ichiro itojun Itoh */ #include #include #include #include #include /* sanity check */ #if BYTE_ORDER != BIG_ENDIAN # if BYTE_ORDER != LITTLE_ENDIAN # define unsupported 1 # endif #endif #ifndef unsupported /* constant table */ static u_int32_t _K[] = { 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6 }; #define K(t) _K[(t) / 20] #define F0(b, c, d) (((b) & (c)) | ((~(b)) & (d))) #define F1(b, c, d) (((b) ^ (c)) ^ (d)) #define F2(b, c, d) (((b) & (c)) | ((b) & (d)) | ((c) & (d))) #define F3(b, c, d) (((b) ^ (c)) ^ (d)) #define S(n, x) (((x) << (n)) | ((x) >> (32 - n))) #define H(n) (ctxt->h.b32[(n)]) #define COUNT (ctxt->count) #define BCOUNT (ctxt->c.b64[0] / 8) #define W(n) (ctxt->m.b32[(n)]) #define PUTBYTE(x) { \ ctxt->m.b8[(COUNT % 64)] = (x); \ COUNT++; \ COUNT %= 64; \ ctxt->c.b64[0] += 8; \ if (COUNT % 64 == 0) \ sha1_step(ctxt); \ } #define PUTPAD(x) { \ ctxt->m.b8[(COUNT % 64)] = (x); \ COUNT++; \ COUNT %= 64; \ if (COUNT % 64 == 0) \ sha1_step(ctxt); \ } static void sha1_step __P((struct sha1_ctxt *)); static void sha1_step(ctxt) struct sha1_ctxt *ctxt; { u_int32_t a, b, c, d, e; size_t t, s; u_int32_t tmp; #if BYTE_ORDER == LITTLE_ENDIAN struct sha1_ctxt tctxt; bcopy(&ctxt->m.b8[0], &tctxt.m.b8[0], 64); ctxt->m.b8[0] = tctxt.m.b8[3]; ctxt->m.b8[1] = tctxt.m.b8[2]; ctxt->m.b8[2] = tctxt.m.b8[1]; ctxt->m.b8[3] = tctxt.m.b8[0]; ctxt->m.b8[4] = tctxt.m.b8[7]; ctxt->m.b8[5] = tctxt.m.b8[6]; ctxt->m.b8[6] = tctxt.m.b8[5]; ctxt->m.b8[7] = tctxt.m.b8[4]; ctxt->m.b8[8] = tctxt.m.b8[11]; ctxt->m.b8[9] = tctxt.m.b8[10]; ctxt->m.b8[10] = tctxt.m.b8[9]; ctxt->m.b8[11] = tctxt.m.b8[8]; ctxt->m.b8[12] = tctxt.m.b8[15]; ctxt->m.b8[13] = tctxt.m.b8[14]; ctxt->m.b8[14] = tctxt.m.b8[13]; ctxt->m.b8[15] = tctxt.m.b8[12]; ctxt->m.b8[16] = tctxt.m.b8[19]; ctxt->m.b8[17] = tctxt.m.b8[18]; ctxt->m.b8[18] = tctxt.m.b8[17]; ctxt->m.b8[19] = tctxt.m.b8[16]; ctxt->m.b8[20] = tctxt.m.b8[23]; ctxt->m.b8[21] = tctxt.m.b8[22]; ctxt->m.b8[22] = tctxt.m.b8[21]; ctxt->m.b8[23] = tctxt.m.b8[20]; ctxt->m.b8[24] = tctxt.m.b8[27]; ctxt->m.b8[25] = tctxt.m.b8[26]; ctxt->m.b8[26] = tctxt.m.b8[25]; ctxt->m.b8[27] = tctxt.m.b8[24]; ctxt->m.b8[28] = tctxt.m.b8[31]; ctxt->m.b8[29] = tctxt.m.b8[30]; ctxt->m.b8[30] = tctxt.m.b8[29]; ctxt->m.b8[31] = tctxt.m.b8[28]; ctxt->m.b8[32] = tctxt.m.b8[35]; ctxt->m.b8[33] = tctxt.m.b8[34]; ctxt->m.b8[34] = tctxt.m.b8[33]; ctxt->m.b8[35] = tctxt.m.b8[32]; ctxt->m.b8[36] = tctxt.m.b8[39]; ctxt->m.b8[37] = tctxt.m.b8[38]; ctxt->m.b8[38] = tctxt.m.b8[37]; ctxt->m.b8[39] = tctxt.m.b8[36]; ctxt->m.b8[40] = tctxt.m.b8[43]; ctxt->m.b8[41] = tctxt.m.b8[42]; ctxt->m.b8[42] = tctxt.m.b8[41]; ctxt->m.b8[43] = tctxt.m.b8[40]; ctxt->m.b8[44] = tctxt.m.b8[47]; ctxt->m.b8[45] = tctxt.m.b8[46]; ctxt->m.b8[46] = tctxt.m.b8[45]; ctxt->m.b8[47] = tctxt.m.b8[44]; ctxt->m.b8[48] = tctxt.m.b8[51]; ctxt->m.b8[49] = tctxt.m.b8[50]; ctxt->m.b8[50] = tctxt.m.b8[49]; ctxt->m.b8[51] = tctxt.m.b8[48]; ctxt->m.b8[52] = tctxt.m.b8[55]; ctxt->m.b8[53] = tctxt.m.b8[54]; ctxt->m.b8[54] = tctxt.m.b8[53]; ctxt->m.b8[55] = tctxt.m.b8[52]; ctxt->m.b8[56] = tctxt.m.b8[59]; ctxt->m.b8[57] = tctxt.m.b8[58]; ctxt->m.b8[58] = tctxt.m.b8[57]; ctxt->m.b8[59] = tctxt.m.b8[56]; ctxt->m.b8[60] = tctxt.m.b8[63]; ctxt->m.b8[61] = tctxt.m.b8[62]; ctxt->m.b8[62] = tctxt.m.b8[61]; ctxt->m.b8[63] = tctxt.m.b8[60]; #endif a = H(0); b = H(1); c = H(2); d = H(3); e = H(4); for (t = 0; t < 20; t++) { s = t & 0x0f; if (t >= 16) { W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^ W((s+2) & 0x0f) ^ W(s)); } tmp = S(5, a) + F0(b, c, d) + e + W(s) + K(t); e = d; d = c; c = S(30, b); b = a; a = tmp; } for (t = 20; t < 40; t++) { s = t & 0x0f; W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^ W((s+2) & 0x0f) ^ W(s)); tmp = S(5, a) + F1(b, c, d) + e + W(s) + K(t); e = d; d = c; c = S(30, b); b = a; a = tmp; } for (t = 40; t < 60; t++) { s = t & 0x0f; W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^ W((s+2) & 0x0f) ^ W(s)); tmp = S(5, a) + F2(b, c, d) + e + W(s) + K(t); e = d; d = c; c = S(30, b); b = a; a = tmp; } for (t = 60; t < 80; t++) { s = t & 0x0f; W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^ W((s+2) & 0x0f) ^ W(s)); tmp = S(5, a) + F3(b, c, d) + e + W(s) + K(t); e = d; d = c; c = S(30, b); b = a; a = tmp; } H(0) = H(0) + a; H(1) = H(1) + b; H(2) = H(2) + c; H(3) = H(3) + d; H(4) = H(4) + e; bzero(&ctxt->m.b8[0], 64); } /*------------------------------------------------------------*/ void sha1_init(ctxt) struct sha1_ctxt *ctxt; { bzero(ctxt, sizeof(struct sha1_ctxt)); H(0) = 0x67452301; H(1) = 0xefcdab89; H(2) = 0x98badcfe; H(3) = 0x10325476; H(4) = 0xc3d2e1f0; } void sha1_pad(ctxt) struct sha1_ctxt *ctxt; { size_t padlen; /*pad length in bytes*/ size_t padstart; PUTPAD(0x80); padstart = COUNT % 64; padlen = 64 - padstart; if (padlen < 8) { bzero(&ctxt->m.b8[padstart], padlen); COUNT += padlen; COUNT %= 64; sha1_step(ctxt); padstart = COUNT % 64; /* should be 0 */ padlen = 64 - padstart; /* should be 64 */ } bzero(&ctxt->m.b8[padstart], padlen - 8); COUNT += (padlen - 8); COUNT %= 64; #if BYTE_ORDER == BIG_ENDIAN PUTPAD(ctxt->c.b8[0]); PUTPAD(ctxt->c.b8[1]); PUTPAD(ctxt->c.b8[2]); PUTPAD(ctxt->c.b8[3]); PUTPAD(ctxt->c.b8[4]); PUTPAD(ctxt->c.b8[5]); PUTPAD(ctxt->c.b8[6]); PUTPAD(ctxt->c.b8[7]); #else PUTPAD(ctxt->c.b8[7]); PUTPAD(ctxt->c.b8[6]); PUTPAD(ctxt->c.b8[5]); PUTPAD(ctxt->c.b8[4]); PUTPAD(ctxt->c.b8[3]); PUTPAD(ctxt->c.b8[2]); PUTPAD(ctxt->c.b8[1]); PUTPAD(ctxt->c.b8[0]); #endif } void -sha1_loop(ctxt, input, len) +sha1_loop(ctxt, input0, len) struct sha1_ctxt *ctxt; - const u_char *input; + const caddr_t input0; size_t len; { + const u_int8_t *input; size_t gaplen; size_t gapstart; size_t off; size_t copysiz; + input = (const u_int8_t *)input0; off = 0; while (off < len) { gapstart = COUNT % 64; gaplen = 64 - gapstart; copysiz = (gaplen < len - off) ? gaplen : len - off; bcopy(&input[off], &ctxt->m.b8[gapstart], copysiz); COUNT += copysiz; COUNT %= 64; ctxt->c.b64[0] += copysiz * 8; if (COUNT % 64 == 0) sha1_step(ctxt); off += copysiz; } } void sha1_result(ctxt, digest0) struct sha1_ctxt *ctxt; caddr_t digest0; { u_int8_t *digest; digest = (u_int8_t *)digest0; sha1_pad(ctxt); #if BYTE_ORDER == BIG_ENDIAN bcopy(&ctxt->h.b8[0], digest, 20); #else digest[0] = ctxt->h.b8[3]; digest[1] = ctxt->h.b8[2]; digest[2] = ctxt->h.b8[1]; digest[3] = ctxt->h.b8[0]; digest[4] = ctxt->h.b8[7]; digest[5] = ctxt->h.b8[6]; digest[6] = ctxt->h.b8[5]; digest[7] = ctxt->h.b8[4]; digest[8] = ctxt->h.b8[11]; digest[9] = ctxt->h.b8[10]; digest[10] = ctxt->h.b8[9]; digest[11] = ctxt->h.b8[8]; digest[12] = ctxt->h.b8[15]; digest[13] = ctxt->h.b8[14]; digest[14] = ctxt->h.b8[13]; digest[15] = ctxt->h.b8[12]; digest[16] = ctxt->h.b8[19]; digest[17] = ctxt->h.b8[18]; digest[18] = ctxt->h.b8[17]; digest[19] = ctxt->h.b8[16]; #endif } #endif /*unsupported*/ Index: head/sys/crypto/sha1.h =================================================================== --- head/sys/crypto/sha1.h (revision 62586) +++ head/sys/crypto/sha1.h (revision 62587) @@ -1,71 +1,72 @@ +/* $FreeBSD$ */ +/* $KAME: sha1.h,v 1.5 2000/03/27 04:36:23 sumikawa Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* * FIPS pub 180-1: Secure Hash Algorithm (SHA-1) * based on: http://csrc.nist.gov/fips/fip180-1.txt * implemented by Jun-ichiro itojun Itoh */ #ifndef _NETINET6_SHA1_H_ -#define _NETINET6_SHA1_H_ +#define _NETINET6_SHA1_H_ struct sha1_ctxt { union { u_int8_t b8[20]; u_int32_t b32[5]; } h; union { u_int8_t b8[8]; u_int64_t b64[1]; } c; union { u_int8_t b8[64]; u_int32_t b32[16]; } m; u_int8_t count; }; #ifdef _KERNEL extern void sha1_init __P((struct sha1_ctxt *)); extern void sha1_pad __P((struct sha1_ctxt *)); -extern void sha1_loop __P((struct sha1_ctxt *, const u_char *, size_t)); +extern void sha1_loop __P((struct sha1_ctxt *, const caddr_t, size_t)); extern void sha1_result __P((struct sha1_ctxt *, caddr_t)); /* compatibilty with other SHA1 source codes */ typedef struct sha1_ctxt SHA1_CTX; -#define SHA1Init(x) sha1_init((x)) -#define SHA1Update(x, y, z) sha1_loop((x), (y), (z)) -#define SHA1Final(x, y) sha1_result((y), (x)) +#define SHA1Init(x) sha1_init((x)) +#define SHA1Update(x, y, z) sha1_loop((x), (y), (z)) +#define SHA1Final(x, y) sha1_result((y), (x)) #endif #define SHA1_RESULTLEN (160/8) #endif /*_NETINET6_SHA1_H_*/ Index: head/sys/kern/uipc_mbuf.c =================================================================== --- head/sys/kern/uipc_mbuf.c (revision 62586) +++ head/sys/kern/uipc_mbuf.c (revision 62587) @@ -1,1185 +1,1194 @@ /* * Copyright (c) 1982, 1986, 1988, 1991, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)uipc_mbuf.c 8.2 (Berkeley) 1/4/94 * $FreeBSD$ */ #include "opt_param.h" #include #include #include #include #include #include #include #include #include #include #include #ifdef INVARIANTS #include #endif static void mbinit __P((void *)); SYSINIT(mbuf, SI_SUB_MBUF, SI_ORDER_FIRST, mbinit, NULL) struct mbuf *mbutl; char *mclrefcnt; struct mbstat mbstat; struct mbuf *mmbfree; union mcluster *mclfree; int max_linkhdr; int max_protohdr; int max_hdr; int max_datalen; int nmbclusters; int nmbufs; u_int m_mballoc_wid = 0; u_int m_clalloc_wid = 0; SYSCTL_DECL(_kern_ipc); SYSCTL_INT(_kern_ipc, KIPC_MAX_LINKHDR, max_linkhdr, CTLFLAG_RW, &max_linkhdr, 0, ""); SYSCTL_INT(_kern_ipc, KIPC_MAX_PROTOHDR, max_protohdr, CTLFLAG_RW, &max_protohdr, 0, ""); SYSCTL_INT(_kern_ipc, KIPC_MAX_HDR, max_hdr, CTLFLAG_RW, &max_hdr, 0, ""); SYSCTL_INT(_kern_ipc, KIPC_MAX_DATALEN, max_datalen, CTLFLAG_RW, &max_datalen, 0, ""); SYSCTL_INT(_kern_ipc, OID_AUTO, mbuf_wait, CTLFLAG_RW, &mbuf_wait, 0, ""); SYSCTL_STRUCT(_kern_ipc, KIPC_MBSTAT, mbstat, CTLFLAG_RW, &mbstat, mbstat, ""); SYSCTL_INT(_kern_ipc, KIPC_NMBCLUSTERS, nmbclusters, CTLFLAG_RD, &nmbclusters, 0, "Maximum number of mbuf clusters available"); SYSCTL_INT(_kern_ipc, OID_AUTO, nmbufs, CTLFLAG_RD, &nmbufs, 0, "Maximum number of mbufs available"); #ifndef NMBCLUSTERS #define NMBCLUSTERS (512 + MAXUSERS * 16) #endif TUNABLE_INT_DECL("kern.ipc.nmbclusters", NMBCLUSTERS, nmbclusters); TUNABLE_INT_DECL("kern.ipc.nmbufs", NMBCLUSTERS * 4, nmbufs); static void m_reclaim __P((void)); /* "number of clusters of pages" */ #define NCL_INIT 1 #define NMB_INIT 16 /* ARGSUSED*/ static void mbinit(dummy) void *dummy; { int s; mmbfree = NULL; mclfree = NULL; mbstat.m_msize = MSIZE; mbstat.m_mclbytes = MCLBYTES; mbstat.m_minclsize = MINCLSIZE; mbstat.m_mlen = MLEN; mbstat.m_mhlen = MHLEN; s = splimp(); if (m_mballoc(NMB_INIT, M_DONTWAIT) == 0) goto bad; #if MCLBYTES <= PAGE_SIZE if (m_clalloc(NCL_INIT, M_DONTWAIT) == 0) goto bad; #else /* It's OK to call contigmalloc in this context. */ if (m_clalloc(16, M_WAIT) == 0) goto bad; #endif splx(s); return; bad: panic("mbinit"); } /* * Allocate at least nmb mbufs and place on mbuf free list. * Must be called at splimp. */ /* ARGSUSED */ int m_mballoc(nmb, how) register int nmb; int how; { register caddr_t p; register int i; int nbytes; /* * If we've hit the mbuf limit, stop allocating from mb_map, * (or trying to) in order to avoid dipping into the section of * mb_map which we've "reserved" for clusters. */ if ((nmb + mbstat.m_mbufs) > nmbufs) return (0); /* * Once we run out of map space, it will be impossible to get * any more (nothing is ever freed back to the map) * -- however you are not dead as m_reclaim might * still be able to free a substantial amount of space. * * XXX Furthermore, we can also work with "recycled" mbufs (when * we're calling with M_WAIT the sleep procedure will be woken * up when an mbuf is freed. See m_mballoc_wait()). */ if (mb_map_full) return (0); nbytes = round_page(nmb * MSIZE); p = (caddr_t)kmem_malloc(mb_map, nbytes, M_NOWAIT); if (p == 0 && how == M_WAIT) { mbstat.m_wait++; p = (caddr_t)kmem_malloc(mb_map, nbytes, M_WAITOK); } /* * Either the map is now full, or `how' is M_NOWAIT and there * are no pages left. */ if (p == NULL) return (0); nmb = nbytes / MSIZE; for (i = 0; i < nmb; i++) { ((struct mbuf *)p)->m_next = mmbfree; mmbfree = (struct mbuf *)p; p += MSIZE; } mbstat.m_mbufs += nmb; return (1); } /* * Once the mb_map has been exhausted and if the call to the allocation macros * (or, in some cases, functions) is with M_WAIT, then it is necessary to rely * solely on reclaimed mbufs. Here we wait for an mbuf to be freed for a * designated (mbuf_wait) time. */ struct mbuf * m_mballoc_wait(int caller, int type) { struct mbuf *p; int s; m_mballoc_wid++; if ((tsleep(&m_mballoc_wid, PVM, "mballc", mbuf_wait)) == EWOULDBLOCK) m_mballoc_wid--; /* * Now that we (think) that we've got something, we will redo an * MGET, but avoid getting into another instance of m_mballoc_wait() * XXX: We retry to fetch _even_ if the sleep timed out. This is left * this way, purposely, in the [unlikely] case that an mbuf was * freed but the sleep was not awakened in time. */ p = NULL; switch (caller) { case MGET_C: MGET(p, M_DONTWAIT, type); break; case MGETHDR_C: MGETHDR(p, M_DONTWAIT, type); break; default: panic("m_mballoc_wait: invalid caller (%d)", caller); } s = splimp(); if (p != NULL) { /* We waited and got something... */ mbstat.m_wait++; /* Wake up another if we have more free. */ if (mmbfree != NULL) MMBWAKEUP(); } splx(s); return (p); } #if MCLBYTES > PAGE_SIZE static int i_want_my_mcl; static void kproc_mclalloc(void) { int status; while (1) { tsleep(&i_want_my_mcl, PVM, "mclalloc", 0); for (; i_want_my_mcl; i_want_my_mcl--) { if (m_clalloc(1, M_WAIT) == 0) printf("m_clalloc failed even in process context!\n"); } } } static struct proc *mclallocproc; static struct kproc_desc mclalloc_kp = { "mclalloc", kproc_mclalloc, &mclallocproc }; SYSINIT(mclallocproc, SI_SUB_KTHREAD_UPDATE, SI_ORDER_ANY, kproc_start, &mclalloc_kp); #endif /* * Allocate some number of mbuf clusters * and place on cluster free list. * Must be called at splimp. */ /* ARGSUSED */ int m_clalloc(ncl, how) register int ncl; int how; { register caddr_t p; register int i; int npg; /* * If we've hit the mcluster number limit, stop allocating from * mb_map, (or trying to) in order to avoid dipping into the section * of mb_map which we've "reserved" for mbufs. */ if ((ncl + mbstat.m_clusters) > nmbclusters) { mbstat.m_drops++; return (0); } /* * Once we run out of map space, it will be impossible * to get any more (nothing is ever freed back to the * map). From this point on, we solely rely on freed * mclusters. */ if (mb_map_full) { mbstat.m_drops++; return (0); } #if MCLBYTES > PAGE_SIZE if (how != M_WAIT) { i_want_my_mcl += ncl; wakeup(&i_want_my_mcl); mbstat.m_wait++; p = 0; } else { p = contigmalloc1(MCLBYTES * ncl, M_DEVBUF, M_WAITOK, 0ul, ~0ul, PAGE_SIZE, 0, mb_map); } #else npg = ncl; p = (caddr_t)kmem_malloc(mb_map, ctob(npg), how != M_WAIT ? M_NOWAIT : M_WAITOK); ncl = ncl * PAGE_SIZE / MCLBYTES; #endif /* * Either the map is now full, or `how' is M_NOWAIT and there * are no pages left. */ if (p == NULL) { mbstat.m_drops++; return (0); } for (i = 0; i < ncl; i++) { ((union mcluster *)p)->mcl_next = mclfree; mclfree = (union mcluster *)p; p += MCLBYTES; mbstat.m_clfree++; } mbstat.m_clusters += ncl; return (1); } /* * Once the mb_map submap has been exhausted and the allocation is called with * M_WAIT, we rely on the mclfree union pointers. If nothing is free, we will * sleep for a designated amount of time (mbuf_wait) or until we're woken up * due to sudden mcluster availability. */ caddr_t m_clalloc_wait(void) { caddr_t p; int s; #ifdef __i386__ /* If in interrupt context, and INVARIANTS, maintain sanity and die. */ KASSERT(intr_nesting_level == 0, ("CLALLOC: CANNOT WAIT IN INTERRUPT")); #endif /* Sleep until something's available or until we expire. */ m_clalloc_wid++; if ((tsleep(&m_clalloc_wid, PVM, "mclalc", mbuf_wait)) == EWOULDBLOCK) m_clalloc_wid--; /* * Now that we (think) that we've got something, we will redo and * MGET, but avoid getting into another instance of m_clalloc_wait() */ p = NULL; MCLALLOC(p, M_DONTWAIT); s = splimp(); if (p != NULL) { /* We waited and got something... */ mbstat.m_wait++; /* Wake up another if we have more free. */ if (mclfree != NULL) MCLWAKEUP(); } splx(s); return (p); } /* * When MGET fails, ask protocols to free space when short of memory, * then re-attempt to allocate an mbuf. */ struct mbuf * m_retry(i, t) int i, t; { register struct mbuf *m; /* * Must only do the reclaim if not in an interrupt context. */ if (i == M_WAIT) { #ifdef __i386__ KASSERT(intr_nesting_level == 0, ("MBALLOC: CANNOT WAIT IN INTERRUPT")); #endif m_reclaim(); } /* * Both m_mballoc_wait and m_retry must be nulled because * when the MGET macro is run from here, we deffinately do _not_ * want to enter an instance of m_mballoc_wait() or m_retry() (again!) */ #define m_mballoc_wait(caller,type) (struct mbuf *)0 #define m_retry(i, t) (struct mbuf *)0 MGET(m, i, t); #undef m_retry #undef m_mballoc_wait if (m != NULL) mbstat.m_wait++; else mbstat.m_drops++; return (m); } /* * As above; retry an MGETHDR. */ struct mbuf * m_retryhdr(i, t) int i, t; { register struct mbuf *m; /* * Must only do the reclaim if not in an interrupt context. */ if (i == M_WAIT) { #ifdef __i386__ KASSERT(intr_nesting_level == 0, ("MBALLOC: CANNOT WAIT IN INTERRUPT")); #endif m_reclaim(); } #define m_mballoc_wait(caller,type) (struct mbuf *)0 #define m_retryhdr(i, t) (struct mbuf *)0 MGETHDR(m, i, t); #undef m_retryhdr #undef m_mballoc_wait if (m != NULL) mbstat.m_wait++; else mbstat.m_drops++; return (m); } static void m_reclaim() { register struct domain *dp; register struct protosw *pr; int s = splimp(); for (dp = domains; dp; dp = dp->dom_next) for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++) if (pr->pr_drain) (*pr->pr_drain)(); splx(s); mbstat.m_drain++; } /* * Space allocation routines. * These are also available as macros * for critical paths. */ struct mbuf * m_get(how, type) int how, type; { register struct mbuf *m; MGET(m, how, type); return (m); } struct mbuf * m_gethdr(how, type) int how, type; { register struct mbuf *m; MGETHDR(m, how, type); return (m); } struct mbuf * m_getclr(how, type) int how, type; { register struct mbuf *m; MGET(m, how, type); if (m == 0) return (0); bzero(mtod(m, caddr_t), MLEN); return (m); } struct mbuf * m_free(m) struct mbuf *m; { register struct mbuf *n; MFREE(m, n); return (n); } void m_freem(m) register struct mbuf *m; { register struct mbuf *n; if (m == NULL) return; do { + /* + * we do need to check non-first mbuf, since some of existing + * code does not call M_PREPEND properly. + * (example: call to bpf_mtap from drivers) + */ + if ((m->m_flags & M_PKTHDR) != 0 && m->m_pkthdr.aux) { + m_freem(m->m_pkthdr.aux); + m->m_pkthdr.aux = NULL; + } MFREE(m, n); m = n; } while (m); } /* * Mbuffer utility routines. */ /* * Lesser-used path for M_PREPEND: * allocate new mbuf to prepend to chain, * copy junk along. */ struct mbuf * m_prepend(m, len, how) register struct mbuf *m; int len, how; { struct mbuf *mn; MGET(mn, how, m->m_type); if (mn == (struct mbuf *)NULL) { m_freem(m); return ((struct mbuf *)NULL); } if (m->m_flags & M_PKTHDR) { M_COPY_PKTHDR(mn, m); m->m_flags &= ~M_PKTHDR; } mn->m_next = m; m = mn; if (len < MHLEN) MH_ALIGN(m, len); m->m_len = len; return (m); } /* * Make a copy of an mbuf chain starting "off0" bytes from the beginning, * continuing for "len" bytes. If len is M_COPYALL, copy to end of mbuf. * The wait parameter is a choice of M_WAIT/M_DONTWAIT from caller. * Note that the copy is read-only, because clusters are not copied, * only their reference counts are incremented. */ #define MCFail (mbstat.m_mcfail) struct mbuf * m_copym(m, off0, len, wait) register struct mbuf *m; int off0, wait; register int len; { register struct mbuf *n, **np; register int off = off0; struct mbuf *top; int copyhdr = 0; KASSERT(off >= 0, ("m_copym, negative off %d", off)); KASSERT(len >= 0, ("m_copym, negative len %d", len)); if (off == 0 && m->m_flags & M_PKTHDR) copyhdr = 1; while (off > 0) { KASSERT(m != NULL, ("m_copym, offset > size of mbuf chain")); if (off < m->m_len) break; off -= m->m_len; m = m->m_next; } np = ⊤ top = 0; while (len > 0) { if (m == 0) { KASSERT(len == M_COPYALL, ("m_copym, length > size of mbuf chain")); break; } MGET(n, wait, m->m_type); *np = n; if (n == 0) goto nospace; if (copyhdr) { M_COPY_PKTHDR(n, m); if (len == M_COPYALL) n->m_pkthdr.len -= off0; else n->m_pkthdr.len = len; copyhdr = 0; } n->m_len = min(len, m->m_len - off); if (m->m_flags & M_EXT) { n->m_data = m->m_data + off; if(!m->m_ext.ext_ref) mclrefcnt[mtocl(m->m_ext.ext_buf)]++; else (*(m->m_ext.ext_ref))(m->m_ext.ext_buf, m->m_ext.ext_size); n->m_ext = m->m_ext; n->m_flags |= M_EXT; } else bcopy(mtod(m, caddr_t)+off, mtod(n, caddr_t), (unsigned)n->m_len); if (len != M_COPYALL) len -= n->m_len; off = 0; m = m->m_next; np = &n->m_next; } if (top == 0) MCFail++; return (top); nospace: m_freem(top); MCFail++; return (0); } /* * Copy an entire packet, including header (which must be present). * An optimization of the common case `m_copym(m, 0, M_COPYALL, how)'. * Note that the copy is read-only, because clusters are not copied, * only their reference counts are incremented. */ struct mbuf * m_copypacket(m, how) struct mbuf *m; int how; { struct mbuf *top, *n, *o; MGET(n, how, m->m_type); top = n; if (!n) goto nospace; M_COPY_PKTHDR(n, m); n->m_len = m->m_len; if (m->m_flags & M_EXT) { n->m_data = m->m_data; if(!m->m_ext.ext_ref) mclrefcnt[mtocl(m->m_ext.ext_buf)]++; else (*(m->m_ext.ext_ref))(m->m_ext.ext_buf, m->m_ext.ext_size); n->m_ext = m->m_ext; n->m_flags |= M_EXT; } else { bcopy(mtod(m, char *), mtod(n, char *), n->m_len); } m = m->m_next; while (m) { MGET(o, how, m->m_type); if (!o) goto nospace; n->m_next = o; n = n->m_next; n->m_len = m->m_len; if (m->m_flags & M_EXT) { n->m_data = m->m_data; if(!m->m_ext.ext_ref) mclrefcnt[mtocl(m->m_ext.ext_buf)]++; else (*(m->m_ext.ext_ref))(m->m_ext.ext_buf, m->m_ext.ext_size); n->m_ext = m->m_ext; n->m_flags |= M_EXT; } else { bcopy(mtod(m, char *), mtod(n, char *), n->m_len); } m = m->m_next; } return top; nospace: m_freem(top); MCFail++; return 0; } /* * Copy data from an mbuf chain starting "off" bytes from the beginning, * continuing for "len" bytes, into the indicated buffer. */ void m_copydata(m, off, len, cp) register struct mbuf *m; register int off; register int len; caddr_t cp; { register unsigned count; KASSERT(off >= 0, ("m_copydata, negative off %d", off)); KASSERT(len >= 0, ("m_copydata, negative len %d", len)); while (off > 0) { KASSERT(m != NULL, ("m_copydata, offset > size of mbuf chain")); if (off < m->m_len) break; off -= m->m_len; m = m->m_next; } while (len > 0) { KASSERT(m != NULL, ("m_copydata, length > size of mbuf chain")); count = min(m->m_len - off, len); bcopy(mtod(m, caddr_t) + off, cp, count); len -= count; cp += count; off = 0; m = m->m_next; } } /* * Copy a packet header mbuf chain into a completely new chain, including * copying any mbuf clusters. Use this instead of m_copypacket() when * you need a writable copy of an mbuf chain. */ struct mbuf * m_dup(m, how) struct mbuf *m; int how; { struct mbuf **p, *top = NULL; int remain, moff, nsize; /* Sanity check */ if (m == NULL) return (0); KASSERT((m->m_flags & M_PKTHDR) != 0, ("%s: !PKTHDR", __FUNCTION__)); /* While there's more data, get a new mbuf, tack it on, and fill it */ remain = m->m_pkthdr.len; moff = 0; p = ⊤ while (remain > 0 || top == NULL) { /* allow m->m_pkthdr.len == 0 */ struct mbuf *n; /* Get the next new mbuf */ MGET(n, how, m->m_type); if (n == NULL) goto nospace; if (top == NULL) { /* first one, must be PKTHDR */ M_COPY_PKTHDR(n, m); nsize = MHLEN; } else /* not the first one */ nsize = MLEN; if (remain >= MINCLSIZE) { MCLGET(n, how); if ((n->m_flags & M_EXT) == 0) { (void)m_free(n); goto nospace; } nsize = MCLBYTES; } n->m_len = 0; /* Link it into the new chain */ *p = n; p = &n->m_next; /* Copy data from original mbuf(s) into new mbuf */ while (n->m_len < nsize && m != NULL) { int chunk = min(nsize - n->m_len, m->m_len - moff); bcopy(m->m_data + moff, n->m_data + n->m_len, chunk); moff += chunk; n->m_len += chunk; remain -= chunk; if (moff == m->m_len) { m = m->m_next; moff = 0; } } /* Check correct total mbuf length */ KASSERT((remain > 0 && m != NULL) || (remain == 0 && m == NULL), ("%s: bogus m_pkthdr.len", __FUNCTION__)); } return (top); nospace: m_freem(top); MCFail++; return (0); } /* * Concatenate mbuf chain n to m. * Both chains must be of the same type (e.g. MT_DATA). * Any m_pkthdr is not updated. */ void m_cat(m, n) register struct mbuf *m, *n; { while (m->m_next) m = m->m_next; while (n) { if (m->m_flags & M_EXT || m->m_data + m->m_len + n->m_len >= &m->m_dat[MLEN]) { /* just join the two chains */ m->m_next = n; return; } /* splat the data from one into the other */ bcopy(mtod(n, caddr_t), mtod(m, caddr_t) + m->m_len, (u_int)n->m_len); m->m_len += n->m_len; n = m_free(n); } } void m_adj(mp, req_len) struct mbuf *mp; int req_len; { register int len = req_len; register struct mbuf *m; register int count; if ((m = mp) == NULL) return; if (len >= 0) { /* * Trim from head. */ while (m != NULL && len > 0) { if (m->m_len <= len) { len -= m->m_len; m->m_len = 0; m = m->m_next; } else { m->m_len -= len; m->m_data += len; len = 0; } } m = mp; if (mp->m_flags & M_PKTHDR) m->m_pkthdr.len -= (req_len - len); } else { /* * Trim from tail. Scan the mbuf chain, * calculating its length and finding the last mbuf. * If the adjustment only affects this mbuf, then just * adjust and return. Otherwise, rescan and truncate * after the remaining size. */ len = -len; count = 0; for (;;) { count += m->m_len; if (m->m_next == (struct mbuf *)0) break; m = m->m_next; } if (m->m_len >= len) { m->m_len -= len; if (mp->m_flags & M_PKTHDR) mp->m_pkthdr.len -= len; return; } count -= len; if (count < 0) count = 0; /* * Correct length for chain is "count". * Find the mbuf with last data, adjust its length, * and toss data from remaining mbufs on chain. */ m = mp; if (m->m_flags & M_PKTHDR) m->m_pkthdr.len = count; for (; m; m = m->m_next) { if (m->m_len >= count) { m->m_len = count; break; } count -= m->m_len; } while (m->m_next) (m = m->m_next) ->m_len = 0; } } /* * Rearange an mbuf chain so that len bytes are contiguous * and in the data area of an mbuf (so that mtod and dtom * will work for a structure of size len). Returns the resulting * mbuf chain on success, frees it and returns null on failure. * If there is room, it will add up to max_protohdr-len extra bytes to the * contiguous region in an attempt to avoid being called next time. */ #define MPFail (mbstat.m_mpfail) struct mbuf * m_pullup(n, len) register struct mbuf *n; int len; { register struct mbuf *m; register int count; int space; /* * If first mbuf has no cluster, and has room for len bytes * without shifting current data, pullup into it, * otherwise allocate a new mbuf to prepend to the chain. */ if ((n->m_flags & M_EXT) == 0 && n->m_data + len < &n->m_dat[MLEN] && n->m_next) { if (n->m_len >= len) return (n); m = n; n = n->m_next; len -= m->m_len; } else { if (len > MHLEN) goto bad; MGET(m, M_DONTWAIT, n->m_type); if (m == 0) goto bad; m->m_len = 0; if (n->m_flags & M_PKTHDR) { M_COPY_PKTHDR(m, n); n->m_flags &= ~M_PKTHDR; } } space = &m->m_dat[MLEN] - (m->m_data + m->m_len); do { count = min(min(max(len, max_protohdr), space), n->m_len); bcopy(mtod(n, caddr_t), mtod(m, caddr_t) + m->m_len, (unsigned)count); len -= count; m->m_len += count; n->m_len -= count; space -= count; if (n->m_len) n->m_data += count; else n = m_free(n); } while (len > 0 && n); if (len > 0) { (void) m_free(m); goto bad; } m->m_next = n; return (m); bad: m_freem(n); MPFail++; return (0); } /* * Partition an mbuf chain in two pieces, returning the tail -- * all but the first len0 bytes. In case of failure, it returns NULL and * attempts to restore the chain to its original state. */ struct mbuf * m_split(m0, len0, wait) register struct mbuf *m0; int len0, wait; { register struct mbuf *m, *n; unsigned len = len0, remain; for (m = m0; m && len > m->m_len; m = m->m_next) len -= m->m_len; if (m == 0) return (0); remain = m->m_len - len; if (m0->m_flags & M_PKTHDR) { MGETHDR(n, wait, m0->m_type); if (n == 0) return (0); n->m_pkthdr.rcvif = m0->m_pkthdr.rcvif; n->m_pkthdr.len = m0->m_pkthdr.len - len0; m0->m_pkthdr.len = len0; if (m->m_flags & M_EXT) goto extpacket; if (remain > MHLEN) { /* m can't be the lead packet */ MH_ALIGN(n, 0); n->m_next = m_split(m, len, wait); if (n->m_next == 0) { (void) m_free(n); return (0); } else return (n); } else MH_ALIGN(n, remain); } else if (remain == 0) { n = m->m_next; m->m_next = 0; return (n); } else { MGET(n, wait, m->m_type); if (n == 0) return (0); M_ALIGN(n, remain); } extpacket: if (m->m_flags & M_EXT) { n->m_flags |= M_EXT; n->m_ext = m->m_ext; if(!m->m_ext.ext_ref) mclrefcnt[mtocl(m->m_ext.ext_buf)]++; else (*(m->m_ext.ext_ref))(m->m_ext.ext_buf, m->m_ext.ext_size); m->m_ext.ext_size = 0; /* For Accounting XXXXXX danger */ n->m_data = m->m_data + len; } else { bcopy(mtod(m, caddr_t) + len, mtod(n, caddr_t), remain); } n->m_len = remain; m->m_len = len; n->m_next = m->m_next; m->m_next = 0; return (n); } /* * Routine to copy from device local memory into mbufs. */ struct mbuf * m_devget(buf, totlen, off0, ifp, copy) char *buf; int totlen, off0; struct ifnet *ifp; void (*copy) __P((char *from, caddr_t to, u_int len)); { register struct mbuf *m; struct mbuf *top = 0, **mp = ⊤ register int off = off0, len; register char *cp; char *epkt; cp = buf; epkt = cp + totlen; if (off) { cp += off + 2 * sizeof(u_short); totlen -= 2 * sizeof(u_short); } MGETHDR(m, M_DONTWAIT, MT_DATA); if (m == 0) return (0); m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = totlen; m->m_len = MHLEN; while (totlen > 0) { if (top) { MGET(m, M_DONTWAIT, MT_DATA); if (m == 0) { m_freem(top); return (0); } m->m_len = MLEN; } len = min(totlen, epkt - cp); if (len >= MINCLSIZE) { MCLGET(m, M_DONTWAIT); if (m->m_flags & M_EXT) m->m_len = len = min(len, MCLBYTES); else len = m->m_len; } else { /* * Place initial small packet/header at end of mbuf. */ if (len < m->m_len) { if (top == 0 && len + max_linkhdr <= m->m_len) m->m_data += max_linkhdr; m->m_len = len; } else len = m->m_len; } if (copy) copy(cp, mtod(m, caddr_t), (unsigned)len); else bcopy(cp, mtod(m, caddr_t), (unsigned)len); cp += len; *mp = m; mp = &m->m_next; totlen -= len; if (cp == epkt) cp = buf; } return (top); } /* * Copy data from a buffer back into the indicated mbuf chain, * starting "off" bytes from the beginning, extending the mbuf * chain if necessary. */ void m_copyback(m0, off, len, cp) struct mbuf *m0; register int off; register int len; caddr_t cp; { register int mlen; register struct mbuf *m = m0, *n; int totlen = 0; if (m0 == 0) return; while (off > (mlen = m->m_len)) { off -= mlen; totlen += mlen; if (m->m_next == 0) { n = m_getclr(M_DONTWAIT, m->m_type); if (n == 0) goto out; n->m_len = min(MLEN, len + off); m->m_next = n; } m = m->m_next; } while (len > 0) { mlen = min (m->m_len - off, len); bcopy(cp, off + mtod(m, caddr_t), (unsigned)mlen); cp += mlen; len -= mlen; mlen += off; off = 0; totlen += mlen; if (len == 0) break; if (m->m_next == 0) { n = m_get(M_DONTWAIT, m->m_type); if (n == 0) break; n->m_len = min(MLEN, len); m->m_next = n; } m = m->m_next; } out: if (((m = m0)->m_flags & M_PKTHDR) && (m->m_pkthdr.len < totlen)) m->m_pkthdr.len = totlen; } void m_print(const struct mbuf *m) { int len; const struct mbuf *m2; len = m->m_pkthdr.len; m2 = m; while (len) { printf("%p %*D\n", m2, m2->m_len, (u_char *)m2->m_data, "-"); len -= m2->m_len; m2 = m2->m_next; } return; } Index: head/sys/kern/uipc_mbuf2.c =================================================================== --- head/sys/kern/uipc_mbuf2.c (nonexistent) +++ head/sys/kern/uipc_mbuf2.c (revision 62587) @@ -0,0 +1,419 @@ +/* $FreeBSD$ */ +/* $KAME: uipc_mbuf2.c,v 1.15 2000/02/22 14:01:37 itojun Exp $ */ +/* $NetBSD: uipc_mbuf.c,v 1.40 1999/04/01 00:23:25 thorpej Exp $ */ + +/* + * Copyright (C) 1999 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Copyright (c) 1982, 1986, 1988, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)uipc_mbuf.c 8.4 (Berkeley) 2/14/95 + */ + +#define PULLDOWN_STAT +/*#define PULLDOWN_DEBUG*/ + +#ifdef PULLDOWN_STAT +#include "opt_inet.h" +#endif + +#include +#include +#include +#include +#include +#if defined(PULLDOWN_STAT) && defined(INET6) +#include +#include +#include +#endif + +/* + * ensure that [off, off + len) is contiguous on the mbuf chain "m". + * packet chain before "off" is kept untouched. + * if offp == NULL, the target will start at on resulting chain. + * if offp != NULL, the target will start at on resulting chain. + * + * on error return (NULL return value), original "m" will be freed. + * + * XXX M_TRAILINGSPACE/M_LEADINGSPACE on shared cluster (sharedcluster) + */ +struct mbuf * +m_pulldown(m, off, len, offp) + struct mbuf *m; + int off, len; + int *offp; +{ + struct mbuf *n, *o; + int hlen, tlen, olen; + int sharedcluster; +#if defined(PULLDOWN_STAT) && defined(INET6) + static struct mbuf *prev = NULL; + int prevlen = 0, prevmlen = 0; +#endif + + /* check invalid arguments. */ + if (m == NULL) + panic("m == NULL in m_pulldown()"); + if (len > MCLBYTES) { + m_freem(m); + return NULL; /* impossible */ + } + +#if defined(PULLDOWN_STAT) && defined(INET6) + ip6stat.ip6s_pulldown++; +#endif + +#if defined(PULLDOWN_STAT) && defined(INET6) + /* statistics for m_pullup */ + ip6stat.ip6s_pullup++; + if (off + len > MHLEN) + ip6stat.ip6s_pullup_fail++; + else { + int dlen, mlen; + + dlen = (prev == m) ? prevlen : m->m_len; + mlen = (prev == m) ? prevmlen : m->m_len + M_TRAILINGSPACE(m); + + if (dlen >= off + len) + ip6stat.ip6s_pullup--; /* call will not be made! */ + else if ((m->m_flags & M_EXT) != 0) { + ip6stat.ip6s_pullup_alloc++; + ip6stat.ip6s_pullup_copy++; + } else { + if (mlen >= off + len) + ip6stat.ip6s_pullup_copy++; + else { + ip6stat.ip6s_pullup_alloc++; + ip6stat.ip6s_pullup_copy++; + } + } + + prevlen = off + len; + prevmlen = MHLEN; + } + + /* statistics for m_pullup2 */ + ip6stat.ip6s_pullup2++; + if (off + len > MCLBYTES) + ip6stat.ip6s_pullup2_fail++; + else { + int dlen, mlen; + + dlen = (prev == m) ? prevlen : m->m_len; + mlen = (prev == m) ? prevmlen : m->m_len + M_TRAILINGSPACE(m); + prevlen = off + len; + prevmlen = mlen; + + if (dlen >= off + len) + ip6stat.ip6s_pullup2--; /* call will not be made! */ + else if ((m->m_flags & M_EXT) != 0) { + ip6stat.ip6s_pullup2_alloc++; + ip6stat.ip6s_pullup2_copy++; + prevmlen = (off + len > MHLEN) ? MCLBYTES : MHLEN; + } else { + if (mlen >= off + len) + ip6stat.ip6s_pullup2_copy++; + else { + ip6stat.ip6s_pullup2_alloc++; + ip6stat.ip6s_pullup2_copy++; + prevmlen = (off + len > MHLEN) ? MCLBYTES + : MHLEN; + } + } + } + + prev = m; +#endif + +#ifdef PULLDOWN_DEBUG + { + struct mbuf *t; + printf("before:"); + for (t = m; t; t = t->m_next) + printf(" %d", t->m_len); + printf("\n"); + } +#endif + n = m; + while (n != NULL && off > 0) { + if (n->m_len > off) + break; + off -= n->m_len; + n = n->m_next; + } + /* be sure to point non-empty mbuf */ + while (n != NULL && n->m_len == 0) + n = n->m_next; + if (!n) { + m_freem(m); + return NULL; /* mbuf chain too short */ + } + + /* + * the target data is on . + * if we got enough data on the mbuf "n", we're done. + */ + if ((off == 0 || offp) && len <= n->m_len - off) + goto ok; + +#if defined(PULLDOWN_STAT) && defined(INET6) + ip6stat.ip6s_pulldown_copy++; +#endif + + /* + * when len < n->m_len - off and off != 0, it is a special case. + * len bytes from sits in single mbuf, but the caller does + * not like the starting position (off). + * chop the current mbuf into two pieces, set off to 0. + */ + if (len < n->m_len - off) { + o = m_copym(n, off, n->m_len - off, M_DONTWAIT); + if (o == NULL) { + m_freem(m); + return NULL; /* ENOBUFS */ + } + n->m_len = off; + o->m_next = n->m_next; + n->m_next = o; + n = n->m_next; + off = 0; + goto ok; + } + + /* + * we need to take hlen from and tlen from m_next, 0>, + * and construct contiguous mbuf with m_len == len. + * note that hlen + tlen == len, and tlen > 0. + */ + hlen = n->m_len - off; + tlen = len - hlen; + + /* + * ensure that we have enough trailing data on mbuf chain. + * if not, we can do nothing about the chain. + */ + olen = 0; + for (o = n->m_next; o != NULL; o = o->m_next) + olen += o->m_len; + if (hlen + olen < len) { + m_freem(m); + return NULL; /* mbuf chain too short */ + } + + /* + * easy cases first. + * we need to use m_copydata() to get data from m_next, 0>. + */ + if ((n->m_flags & M_EXT) == 0) + sharedcluster = 0; + else { + if (n->m_ext.ext_free) + sharedcluster = 1; + else if (mclrefcnt[mtocl(n->m_ext.ext_buf)] > 1) + sharedcluster = 1; + else + sharedcluster = 0; + } + if ((off == 0 || offp) && M_TRAILINGSPACE(n) >= tlen + && !sharedcluster) { + m_copydata(n->m_next, 0, tlen, mtod(n, caddr_t) + n->m_len); + n->m_len += tlen; + m_adj(n->m_next, tlen); + goto ok; + } + if ((off == 0 || offp) && M_LEADINGSPACE(n->m_next) >= hlen + && !sharedcluster) { + n->m_next->m_data -= hlen; + n->m_next->m_len += hlen; + bcopy(mtod(n, caddr_t) + off, mtod(n->m_next, caddr_t), hlen); + n->m_len -= hlen; + n = n->m_next; + off = 0; + goto ok; + } + + /* + * now, we need to do the hard way. don't m_copy as there's no room + * on both end. + */ +#if defined(PULLDOWN_STAT) && defined(INET6) + ip6stat.ip6s_pulldown_alloc++; +#endif + MGET(o, M_DONTWAIT, m->m_type); + if (o == NULL) { + m_freem(m); + return NULL; /* ENOBUFS */ + } + if (len > MHLEN) { /* use MHLEN just for safety */ + MCLGET(o, M_DONTWAIT); + if ((o->m_flags & M_EXT) == 0) { + m_freem(m); + m_free(o); + return NULL; /* ENOBUFS */ + } + } + /* get hlen from into */ + o->m_len = hlen; + bcopy(mtod(n, caddr_t) + off, mtod(o, caddr_t), hlen); + n->m_len -= hlen; + /* get tlen from m_next, 0> into */ + m_copydata(n->m_next, 0, tlen, mtod(o, caddr_t) + o->m_len); + o->m_len += tlen; + m_adj(n->m_next, tlen); + o->m_next = n->m_next; + n->m_next = o; + n = o; + off = 0; + +ok: +#ifdef PULLDOWN_DEBUG + { + struct mbuf *t; + printf("after:"); + for (t = m; t; t = t->m_next) + printf("%c%d", t == n ? '*' : ' ', t->m_len); + printf(" (off=%d)\n", off); + } +#endif + if (offp) + *offp = off; + return n; +} + +/* + * pkthdr.aux chain manipulation. + * we don't allow clusters at this moment. + */ +struct mbuf * +m_aux_add(m, af, type) + struct mbuf *m; + int af, type; +{ + struct mbuf *n; + struct mauxtag *t; + + if ((m->m_flags & M_PKTHDR) == 0) + return NULL; + + n = m_aux_find(m, af, type); + if (n) + return n; + + MGET(n, M_DONTWAIT, m->m_type); + if (n == NULL) + return NULL; + + t = mtod(n, struct mauxtag *); + t->af = af; + t->type = type; + n->m_data += sizeof(struct mauxtag); + n->m_len = 0; + n->m_next = m->m_pkthdr.aux; + m->m_pkthdr.aux = n; + return n; +} + +struct mbuf * +m_aux_find(m, af, type) + struct mbuf *m; + int af, type; +{ + struct mbuf *n; + struct mauxtag *t; + + if ((m->m_flags & M_PKTHDR) == 0) + return NULL; + + for (n = m->m_pkthdr.aux; n; n = n->m_next) { + t = (struct mauxtag *)n->m_dat; + if (t->af == af && t->type == type) + return n; + } + return NULL; +} + +void +m_aux_delete(m, victim) + struct mbuf *m; + struct mbuf *victim; +{ + struct mbuf *n, *prev, *next; + struct mauxtag *t; + + if ((m->m_flags & M_PKTHDR) == 0) + return; + + prev = NULL; + n = m->m_pkthdr.aux; + while (n) { + t = (struct mauxtag *)n->m_dat; + next = n->m_next; + if (n == victim) { + if (prev) + prev->m_next = n->m_next; + else + m->m_pkthdr.aux = n->m_next; + n->m_next = NULL; + m_free(n); + } else + prev = n; + n = next; + } +} Property changes on: head/sys/kern/uipc_mbuf2.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: head/sys/net/if_ethersubr.c =================================================================== --- head/sys/net/if_ethersubr.c (revision 62586) +++ head/sys/net/if_ethersubr.c (revision 62587) @@ -1,856 +1,852 @@ /* * Copyright (c) 1982, 1989, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)if_ethersubr.c 8.1 (Berkeley) 6/10/93 * $FreeBSD$ */ #include "opt_atalk.h" #include "opt_inet.h" #include "opt_inet6.h" #include "opt_ipx.h" #include "opt_bdg.h" #include "opt_netgraph.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(INET) || defined(INET6) #include #include #include #endif #ifdef INET6 #include -#include #endif #ifdef IPX #include #include int (*ef_inputp)(struct ifnet*, struct ether_header *eh, struct mbuf *m); int (*ef_outputp)(struct ifnet *ifp, struct mbuf **mp, struct sockaddr *dst, short *tp); #endif #ifdef NS #include #include ushort ns_nettype; int ether_outputdebug = 0; int ether_inputdebug = 0; #endif #ifdef NETATALK #include #include #include #define llc_snap_org_code llc_un.type_snap.org_code #define llc_snap_ether_type llc_un.type_snap.ether_type extern u_char at_org_code[3]; extern u_char aarp_org_code[3]; #endif /* NETATALK */ #ifdef BRIDGE #include #endif #include "vlan.h" #if NVLAN > 0 #include #endif /* NVLAN > 0 */ /* netgraph node hooks for ng_ether(4) */ void (*ng_ether_input_p)(struct ifnet *ifp, struct mbuf **mp, struct ether_header *eh); void (*ng_ether_input_orphan_p)(struct ifnet *ifp, struct mbuf *m, struct ether_header *eh); int (*ng_ether_output_p)(struct ifnet *ifp, struct mbuf **mp); void (*ng_ether_attach_p)(struct ifnet *ifp); void (*ng_ether_detach_p)(struct ifnet *ifp); static int ether_resolvemulti __P((struct ifnet *, struct sockaddr **, struct sockaddr *)); u_char etherbroadcastaddr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; #define senderr(e) do { error = (e); goto bad;} while (0) #define IFP2AC(IFP) ((struct arpcom *)IFP) /* * Ethernet output routine. * Encapsulate a packet of type family for the local net. * Use trailer local net encapsulation if enough data in first * packet leaves a multiple of 512 bytes of data in remainder. * Assumes that ifp is actually pointer to arpcom structure. */ int ether_output(ifp, m, dst, rt0) register struct ifnet *ifp; struct mbuf *m; struct sockaddr *dst; struct rtentry *rt0; { short type; int error = 0, hdrcmplt = 0; u_char esrc[6], edst[6]; register struct rtentry *rt; register struct ether_header *eh; int off, loop_copy = 0; int hlen; /* link layer header lenght */ struct arpcom *ac = IFP2AC(ifp); if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) senderr(ENETDOWN); rt = rt0; if (rt) { if ((rt->rt_flags & RTF_UP) == 0) { rt0 = rt = rtalloc1(dst, 1, 0UL); if (rt0) rt->rt_refcnt--; else senderr(EHOSTUNREACH); } if (rt->rt_flags & RTF_GATEWAY) { if (rt->rt_gwroute == 0) goto lookup; if (((rt = rt->rt_gwroute)->rt_flags & RTF_UP) == 0) { rtfree(rt); rt = rt0; lookup: rt->rt_gwroute = rtalloc1(rt->rt_gateway, 1, 0UL); if ((rt = rt->rt_gwroute) == 0) senderr(EHOSTUNREACH); } } if (rt->rt_flags & RTF_REJECT) if (rt->rt_rmx.rmx_expire == 0 || time_second < rt->rt_rmx.rmx_expire) senderr(rt == rt0 ? EHOSTDOWN : EHOSTUNREACH); } hlen = ETHER_HDR_LEN; switch (dst->sa_family) { #ifdef INET case AF_INET: if (!arpresolve(ac, rt, m, dst, edst, rt0)) return (0); /* if not yet resolved */ off = m->m_pkthdr.len - m->m_len; type = htons(ETHERTYPE_IP); break; #endif #ifdef INET6 case AF_INET6: if (!nd6_storelladdr(&ac->ac_if, rt, m, dst, (u_char *)edst)) { /* this must be impossible, so we bark */ printf("nd6_storelladdr failed\n"); return(0); } off = m->m_pkthdr.len - m->m_len; type = htons(ETHERTYPE_IPV6); break; #endif #ifdef IPX case AF_IPX: if (ef_outputp) { error = ef_outputp(ifp, &m, dst, &type); if (error) goto bad; } else type = htons(ETHERTYPE_IPX); bcopy((caddr_t)&(((struct sockaddr_ipx *)dst)->sipx_addr.x_host), (caddr_t)edst, sizeof (edst)); break; #endif #ifdef NETATALK case AF_APPLETALK: { struct at_ifaddr *aa; if ((aa = at_ifawithnet((struct sockaddr_at *)dst)) == NULL) { goto bad; } if (!aarpresolve(ac, m, (struct sockaddr_at *)dst, edst)) return (0); /* * In the phase 2 case, need to prepend an mbuf for the llc header. * Since we must preserve the value of m, which is passed to us by * value, we m_copy() the first mbuf, and use it for our llc header. */ if ( aa->aa_flags & AFA_PHASE2 ) { struct llc llc; M_PREPEND(m, sizeof(struct llc), M_WAIT); llc.llc_dsap = llc.llc_ssap = LLC_SNAP_LSAP; llc.llc_control = LLC_UI; bcopy(at_org_code, llc.llc_snap_org_code, sizeof(at_org_code)); llc.llc_snap_ether_type = htons( ETHERTYPE_AT ); bcopy(&llc, mtod(m, caddr_t), sizeof(struct llc)); type = htons(m->m_pkthdr.len); hlen = sizeof(struct llc) + ETHER_HDR_LEN; } else { type = htons(ETHERTYPE_AT); } break; } #endif NETATALK #ifdef NS case AF_NS: switch(ns_nettype){ default: case 0x8137: /* Novell Ethernet_II Ethernet TYPE II */ type = 0x8137; break; case 0x0: /* Novell 802.3 */ type = htons( m->m_pkthdr.len); break; case 0xe0e0: /* Novell 802.2 and Token-Ring */ M_PREPEND(m, 3, M_WAIT); type = htons( m->m_pkthdr.len); cp = mtod(m, u_char *); *cp++ = 0xE0; *cp++ = 0xE0; *cp++ = 0x03; break; } bcopy((caddr_t)&(((struct sockaddr_ns *)dst)->sns_addr.x_host), (caddr_t)edst, sizeof (edst)); /* * XXX if ns_thishost is the same as the node's ethernet * address then just the default code will catch this anyhow. * So I'm not sure if this next clause should be here at all? * [JRE] */ if (!bcmp((caddr_t)edst, (caddr_t)&ns_thishost, sizeof(edst))){ m->m_pkthdr.rcvif = ifp; schednetisr(NETISR_NS); inq = &nsintrq; s = splimp(); if (IF_QFULL(inq)) { IF_DROP(inq); m_freem(m); } else IF_ENQUEUE(inq, m); splx(s); return (error); } if (!bcmp((caddr_t)edst, (caddr_t)&ns_broadhost, sizeof(edst))){ m->m_flags |= M_BCAST; } break; #endif /* NS */ case pseudo_AF_HDRCMPLT: hdrcmplt = 1; eh = (struct ether_header *)dst->sa_data; (void)memcpy(esrc, eh->ether_shost, sizeof (esrc)); /* FALLTHROUGH */ case AF_UNSPEC: loop_copy = -1; /* if this is for us, don't do it */ eh = (struct ether_header *)dst->sa_data; (void)memcpy(edst, eh->ether_dhost, sizeof (edst)); type = eh->ether_type; break; default: printf("%s%d: can't handle af%d\n", ifp->if_name, ifp->if_unit, dst->sa_family); senderr(EAFNOSUPPORT); } /* * Add local net header. If no space in first mbuf, * allocate another. */ M_PREPEND(m, sizeof (struct ether_header), M_DONTWAIT); if (m == 0) senderr(ENOBUFS); eh = mtod(m, struct ether_header *); (void)memcpy(&eh->ether_type, &type, sizeof(eh->ether_type)); (void)memcpy(eh->ether_dhost, edst, sizeof (edst)); if (hdrcmplt) (void)memcpy(eh->ether_shost, esrc, sizeof(eh->ether_shost)); else (void)memcpy(eh->ether_shost, ac->ac_enaddr, sizeof(eh->ether_shost)); /* * If a simplex interface, and the packet is being sent to our * Ethernet address or a broadcast address, loopback a copy. * XXX To make a simplex device behave exactly like a duplex * device, we should copy in the case of sending to our own * ethernet address (thus letting the original actually appear * on the wire). However, we don't do that here for security * reasons and compatibility with the original behavior. */ if ((ifp->if_flags & IFF_SIMPLEX) && (loop_copy != -1)) { if ((m->m_flags & M_BCAST) || (loop_copy > 0)) { struct mbuf *n = m_copy(m, 0, (int)M_COPYALL); (void) if_simloop(ifp, n, dst->sa_family, hlen); } else if (bcmp(eh->ether_dhost, eh->ether_shost, ETHER_ADDR_LEN) == 0) { (void) if_simloop(ifp, m, dst->sa_family, hlen); return (0); /* XXX */ } } /* Handle ng_ether(4) processing, if any */ if (ng_ether_output_p != NULL) { if ((error = (*ng_ether_output_p)(ifp, &m)) != 0) { bad: if (m != NULL) m_freem(m); return (error); } if (m == NULL) return (0); } /* Continue with link-layer output */ return ether_output_frame(ifp, m); } /* * Ethernet link layer output routine to send a raw frame to the device. * * This assumes that the 14 byte Ethernet header is present and contiguous * in the first mbuf (if BRIDGE'ing). */ int ether_output_frame(ifp, m) struct ifnet *ifp; struct mbuf *m; { int s, error = 0; #ifdef BRIDGE if (do_bridge) { struct ether_header hdr; m->m_pkthdr.rcvif = NULL; bcopy(mtod(m, struct ether_header *), &hdr, ETHER_HDR_LEN); m_adj(m, ETHER_HDR_LEN); ifp = bridge_dst_lookup(&hdr); bdg_forward(&m, &hdr, ifp); if (m != NULL) m_freem(m); return (0); } #endif s = splimp(); /* * Queue message on interface, and start output if interface * not yet active. */ if (IF_QFULL(&ifp->if_snd)) { IF_DROP(&ifp->if_snd); splx(s); m_freem(m); return (ENOBUFS); } ifp->if_obytes += m->m_pkthdr.len; if (m->m_flags & M_MCAST) ifp->if_omcasts++; IF_ENQUEUE(&ifp->if_snd, m); if ((ifp->if_flags & IFF_OACTIVE) == 0) (*ifp->if_start)(ifp); splx(s); return (error); } /* * Process a received Ethernet packet; * the packet is in the mbuf chain m without * the ether header, which is provided separately. * * First we perform any link layer operations, then continue * to the upper layers with ether_demux(). */ void ether_input(ifp, eh, m) struct ifnet *ifp; struct ether_header *eh; struct mbuf *m; { /* Check for a BPF tap */ if (ifp->if_bpf != NULL) { struct m_hdr mh; /* This kludge is OK; BPF treats the "mbuf" as read-only */ mh.mh_next = m; mh.mh_data = (char *)eh; mh.mh_len = ETHER_HDR_LEN; bpf_mtap(ifp, (struct mbuf *)&mh); } /* Handle ng_ether(4) processing, if any */ if (ng_ether_input_p != NULL) { (*ng_ether_input_p)(ifp, &m, eh); if (m == NULL) return; } #ifdef BRIDGE /* Check for bridging mode */ if (do_bridge) { struct ifnet *bif; /* Check with bridging code */ if ((bif = bridge_in(ifp, eh)) == BDG_DROP) { m_freem(m); return; } if (bif != BDG_LOCAL) bdg_forward(&m, eh, bif); /* needs forwarding */ if (bif == BDG_LOCAL || bif == BDG_BCAST || bif == BDG_MCAST) goto recvLocal; /* receive locally */ /* If not local and not multicast, just drop it */ if (m != NULL) m_freem(m); return; } #endif /* Discard packet if upper layers shouldn't see it. This should only happen when the interface is in promiscuous mode. */ if ((ifp->if_flags & IFF_PROMISC) != 0 && (eh->ether_dhost[0] & 1) == 0 && bcmp(eh->ether_dhost, IFP2AC(ifp)->ac_enaddr, ETHER_ADDR_LEN) != 0) { m_freem(m); return; } #ifdef BRIDGE recvLocal: #endif /* Continue with upper layer processing */ ether_demux(ifp, eh, m); } /* * Upper layer processing for a received Ethernet packet. */ void ether_demux(ifp, eh, m) struct ifnet *ifp; struct ether_header *eh; struct mbuf *m; { struct ifqueue *inq; u_short ether_type; int s; #if defined(NETATALK) register struct llc *l; #endif /* Discard packet if interface is not up */ if ((ifp->if_flags & IFF_UP) == 0) { m_freem(m); return; } ifp->if_ibytes += m->m_pkthdr.len + sizeof (*eh); if (eh->ether_dhost[0] & 1) { if (bcmp((caddr_t)etherbroadcastaddr, (caddr_t)eh->ether_dhost, sizeof(etherbroadcastaddr)) == 0) m->m_flags |= M_BCAST; else m->m_flags |= M_MCAST; } if (m->m_flags & (M_BCAST|M_MCAST)) ifp->if_imcasts++; ether_type = ntohs(eh->ether_type); #if NVLAN > 0 if (ether_type == vlan_proto) { if (vlan_input(eh, m) < 0) ifp->if_data.ifi_noproto++; return; } #endif /* NVLAN > 0 */ switch (ether_type) { #ifdef INET case ETHERTYPE_IP: if (ipflow_fastforward(m)) return; schednetisr(NETISR_IP); inq = &ipintrq; break; case ETHERTYPE_ARP: schednetisr(NETISR_ARP); inq = &arpintrq; break; #endif #ifdef IPX case ETHERTYPE_IPX: if (ef_inputp && ef_inputp(ifp, eh, m) == 0) return; schednetisr(NETISR_IPX); inq = &ipxintrq; break; #endif #ifdef INET6 case ETHERTYPE_IPV6: schednetisr(NETISR_IPV6); inq = &ip6intrq; break; #endif #ifdef NS case 0x8137: /* Novell Ethernet_II Ethernet TYPE II */ schednetisr(NETISR_NS); inq = &nsintrq; break; #endif /* NS */ #ifdef NETATALK case ETHERTYPE_AT: schednetisr(NETISR_ATALK); inq = &atintrq1; break; case ETHERTYPE_AARP: /* probably this should be done with a NETISR as well */ aarpinput(IFP2AC(ifp), m); /* XXX */ return; #endif NETATALK default: #ifdef IPX if (ef_inputp && ef_inputp(ifp, eh, m) == 0) return; #endif /* IPX */ #ifdef NS checksum = mtod(m, ushort *); /* Novell 802.3 */ if ((ether_type <= ETHERMTU) && ((*checksum == 0xffff) || (*checksum == 0xE0E0))){ if(*checksum == 0xE0E0) { m->m_pkthdr.len -= 3; m->m_len -= 3; m->m_data += 3; } schednetisr(NETISR_NS); inq = &nsintrq; break; } #endif /* NS */ #if defined(NETATALK) if (ether_type > ETHERMTU) goto dropanyway; l = mtod(m, struct llc *); switch (l->llc_dsap) { case LLC_SNAP_LSAP: switch (l->llc_control) { case LLC_UI: if (l->llc_ssap != LLC_SNAP_LSAP) goto dropanyway; if (Bcmp(&(l->llc_snap_org_code)[0], at_org_code, sizeof(at_org_code)) == 0 && ntohs(l->llc_snap_ether_type) == ETHERTYPE_AT) { inq = &atintrq2; m_adj( m, sizeof( struct llc )); schednetisr(NETISR_ATALK); break; } if (Bcmp(&(l->llc_snap_org_code)[0], aarp_org_code, sizeof(aarp_org_code)) == 0 && ntohs(l->llc_snap_ether_type) == ETHERTYPE_AARP) { m_adj( m, sizeof( struct llc )); aarpinput(IFP2AC(ifp), m); /* XXX */ return; } default: goto dropanyway; } break; dropanyway: default: if (ng_ether_input_orphan_p != NULL) (*ng_ether_input_orphan_p)(ifp, m, eh); else m_freem(m); return; } #else /* NETATALK */ if (ng_ether_input_orphan_p != NULL) (*ng_ether_input_orphan_p)(ifp, m, eh); else m_freem(m); return; #endif /* NETATALK */ } s = splimp(); if (IF_QFULL(inq)) { IF_DROP(inq); m_freem(m); } else IF_ENQUEUE(inq, m); splx(s); } /* * Perform common duties while attaching to interface list */ void ether_ifattach(ifp) register struct ifnet *ifp; { register struct ifaddr *ifa; register struct sockaddr_dl *sdl; ifp->if_type = IFT_ETHER; ifp->if_addrlen = 6; ifp->if_hdrlen = 14; ifp->if_mtu = ETHERMTU; ifp->if_resolvemulti = ether_resolvemulti; if (ifp->if_baudrate == 0) ifp->if_baudrate = 10000000; ifa = ifnet_addrs[ifp->if_index - 1]; if (ifa == 0) { printf("ether_ifattach: no lladdr!\n"); return; } sdl = (struct sockaddr_dl *)ifa->ifa_addr; sdl->sdl_type = IFT_ETHER; sdl->sdl_alen = ifp->if_addrlen; bcopy((IFP2AC(ifp))->ac_enaddr, LLADDR(sdl), ifp->if_addrlen); -#ifdef INET6 - in6_ifattach_getifid(ifp); -#endif if (ng_ether_attach_p != NULL) (*ng_ether_attach_p)(ifp); } SYSCTL_DECL(_net_link); SYSCTL_NODE(_net_link, IFT_ETHER, ether, CTLFLAG_RW, 0, "Ethernet"); int ether_ioctl(ifp, command, data) struct ifnet *ifp; int command; caddr_t data; { struct ifaddr *ifa = (struct ifaddr *) data; struct ifreq *ifr = (struct ifreq *) data; int error = 0; switch (command) { case SIOCSIFADDR: ifp->if_flags |= IFF_UP; switch (ifa->ifa_addr->sa_family) { #ifdef INET case AF_INET: ifp->if_init(ifp->if_softc); /* before arpwhohas */ arp_ifinit(IFP2AC(ifp), ifa); break; #endif #ifdef IPX /* * XXX - This code is probably wrong */ case AF_IPX: { register struct ipx_addr *ina = &(IA_SIPX(ifa)->sipx_addr); struct arpcom *ac = IFP2AC(ifp); if (ipx_nullhost(*ina)) ina->x_host = *(union ipx_host *) ac->ac_enaddr; else { bcopy((caddr_t) ina->x_host.c_host, (caddr_t) ac->ac_enaddr, sizeof(ac->ac_enaddr)); } /* * Set new address */ ifp->if_init(ifp->if_softc); break; } #endif #ifdef NS /* * XXX - This code is probably wrong */ case AF_NS: { register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr); struct arpcom *ac = IFP2AC(ifp); if (ns_nullhost(*ina)) ina->x_host = *(union ns_host *) (ac->ac_enaddr); else { bcopy((caddr_t) ina->x_host.c_host, (caddr_t) ac->ac_enaddr, sizeof(ac->ac_enaddr)); } /* * Set new address */ ifp->if_init(ifp->if_softc); break; } #endif default: ifp->if_init(ifp->if_softc); break; } break; case SIOCGIFADDR: { struct sockaddr *sa; sa = (struct sockaddr *) & ifr->ifr_data; bcopy(IFP2AC(ifp)->ac_enaddr, (caddr_t) sa->sa_data, ETHER_ADDR_LEN); } break; case SIOCSIFMTU: /* * Set the interface MTU. */ if (ifr->ifr_mtu > ETHERMTU) { error = EINVAL; } else { ifp->if_mtu = ifr->ifr_mtu; } break; } return (error); } int ether_resolvemulti(ifp, llsa, sa) struct ifnet *ifp; struct sockaddr **llsa; struct sockaddr *sa; { struct sockaddr_dl *sdl; struct sockaddr_in *sin; #ifdef INET6 struct sockaddr_in6 *sin6; #endif u_char *e_addr; switch(sa->sa_family) { case AF_LINK: /* * No mapping needed. Just check that it's a valid MC address. */ sdl = (struct sockaddr_dl *)sa; e_addr = LLADDR(sdl); if ((e_addr[0] & 1) != 1) return EADDRNOTAVAIL; *llsa = 0; return 0; #ifdef INET case AF_INET: sin = (struct sockaddr_in *)sa; if (!IN_MULTICAST(ntohl(sin->sin_addr.s_addr))) return EADDRNOTAVAIL; MALLOC(sdl, struct sockaddr_dl *, sizeof *sdl, M_IFMADDR, M_WAITOK); sdl->sdl_len = sizeof *sdl; sdl->sdl_family = AF_LINK; sdl->sdl_index = ifp->if_index; sdl->sdl_type = IFT_ETHER; sdl->sdl_nlen = 0; sdl->sdl_alen = ETHER_ADDR_LEN; sdl->sdl_slen = 0; e_addr = LLADDR(sdl); ETHER_MAP_IP_MULTICAST(&sin->sin_addr, e_addr); *llsa = (struct sockaddr *)sdl; return 0; #endif #ifdef INET6 case AF_INET6: sin6 = (struct sockaddr_in6 *)sa; if (!IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) return EADDRNOTAVAIL; MALLOC(sdl, struct sockaddr_dl *, sizeof *sdl, M_IFMADDR, M_WAITOK); sdl->sdl_len = sizeof *sdl; sdl->sdl_family = AF_LINK; sdl->sdl_index = ifp->if_index; sdl->sdl_type = IFT_ETHER; sdl->sdl_nlen = 0; sdl->sdl_alen = ETHER_ADDR_LEN; sdl->sdl_slen = 0; e_addr = LLADDR(sdl); ETHER_MAP_IPV6_MULTICAST(&sin6->sin6_addr, e_addr); *llsa = (struct sockaddr *)sdl; return 0; #endif default: /* * Well, the text isn't quite right, but it's the name * that counts... */ return EAFNOSUPPORT; } } Index: head/sys/net/if_gif.c =================================================================== --- head/sys/net/if_gif.c (revision 62586) +++ head/sys/net/if_gif.c (revision 62587) @@ -1,486 +1,629 @@ +/* $FreeBSD$ */ +/* $KAME: if_gif.c,v 1.28 2000/06/20 12:30:03 jinmei Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ -/* - * gif.c - */ - #include "opt_inet.h" #include "opt_inet6.h" #include #include #include #include #include #include #include #include #include #include +#include #include #include #include #include #include #include #ifdef INET #include #include #include #include #include #endif /* INET */ #ifdef INET6 #ifndef INET #include #endif #include #include #include #include +#include #endif /* INET6 */ +#include #include #include "gif.h" +#include "bpf.h" +#define NBPFILTER NBPF #include +#if NGIF > 0 + void gifattach __P((void *)); +static int gif_encapcheck __P((const struct mbuf *, int, int, void *)); +#ifdef INET +extern struct protosw in_gif_protosw; +#endif +#ifdef INET6 +extern struct ip6protosw in6_gif_protosw; +#endif /* * gif global variable definitions */ -int ngif = NGIF + 1; /* number of interfaces. +1 for stf. */ -struct gif_softc *gif = 0; +static int ngif; /* number of interfaces */ +static struct gif_softc *gif = 0; +#ifndef MAX_GIF_NEST +/* + * This macro controls the upper limitation on nesting of gif tunnels. + * Since, setting a large value to this macro with a careless configuration + * may introduce system crash, we don't allow any nestings by default. + * If you need to configure nested gif tunnels, you can define this macro + * in your kernel configuration file. However, if you do so, please be + * careful to configure the tunnels so that it won't make a loop. + */ +#define MAX_GIF_NEST 1 +#endif +static int max_gif_nesting = MAX_GIF_NEST; + void gifattach(dummy) void *dummy; { register struct gif_softc *sc; register int i; + ngif = NGIF; gif = sc = malloc (ngif * sizeof(struct gif_softc), M_DEVBUF, M_WAIT); bzero(sc, ngif * sizeof(struct gif_softc)); - for (i = 0; i < ngif - 1; sc++, i++) { /* leave last one for stf */ + for (i = 0; i < ngif; sc++, i++) { sc->gif_if.if_name = "gif"; sc->gif_if.if_unit = i; + + sc->encap_cookie4 = sc->encap_cookie6 = NULL; +#ifdef INET + sc->encap_cookie4 = encap_attach_func(AF_INET, -1, + gif_encapcheck, &in_gif_protosw, sc); + if (sc->encap_cookie4 == NULL) { + printf("%s: attach failed\n", if_name(&sc->gif_if)); + continue; + } +#endif +#ifdef INET6 + sc->encap_cookie6 = encap_attach_func(AF_INET6, -1, + gif_encapcheck, (struct protosw *)&in6_gif_protosw, sc); + if (sc->encap_cookie6 == NULL) { + if (sc->encap_cookie4) { + encap_detach(sc->encap_cookie4); + sc->encap_cookie4 = NULL; + } + printf("%s: attach failed\n", if_name(&sc->gif_if)); + continue; + } +#endif + sc->gif_if.if_mtu = GIF_MTU; sc->gif_if.if_flags = IFF_POINTOPOINT | IFF_MULTICAST; sc->gif_if.if_ioctl = gif_ioctl; sc->gif_if.if_output = gif_output; sc->gif_if.if_type = IFT_GIF; - sc->gif_if.if_snd.ifq_maxlen = ifqmaxlen; + sc->gif_if.if_snd.ifq_maxlen = IFQ_MAXLEN; if_attach(&sc->gif_if); +#if NBPFILTER > 0 +#ifdef HAVE_OLD_BPF bpfattach(&sc->gif_if, DLT_NULL, sizeof(u_int)); +#else + bpfattach(&sc->gif_if.if_bpf, &sc->gif_if, DLT_NULL, sizeof(u_int)); +#endif +#endif } - sc->gif_if.if_name = "stf"; - sc->gif_if.if_unit = 0; - sc->gif_if.if_mtu = GIF_MTU; - sc->gif_if.if_flags = IFF_MULTICAST; - sc->gif_if.if_ioctl = gif_ioctl; - sc->gif_if.if_output = gif_output; - sc->gif_if.if_type = IFT_GIF; - sc->gif_if.if_snd.ifq_maxlen = ifqmaxlen; - if_attach(&sc->gif_if); - bpfattach(&sc->gif_if, DLT_NULL, sizeof(u_int)); } PSEUDO_SET(gifattach, if_gif); +static int +gif_encapcheck(m, off, proto, arg) + const struct mbuf *m; + int off; + int proto; + void *arg; +{ + struct ip ip; + struct gif_softc *sc; + + sc = (struct gif_softc *)arg; + if (sc == NULL) + return 0; + + if ((sc->gif_if.if_flags & IFF_UP) == 0) + return 0; + + /* no physical address */ + if (!sc->gif_psrc || !sc->gif_pdst) + return 0; + + switch (proto) { +#ifdef INET + case IPPROTO_IPV4: + break; +#endif +#ifdef INET6 + case IPPROTO_IPV6: + break; +#endif + default: + return 0; + } + + /* LINTED const cast */ + m_copydata((struct mbuf *)m, 0, sizeof(ip), (caddr_t)&ip); + + switch (ip.ip_v) { +#ifdef INET + case 4: + if (sc->gif_psrc->sa_family != AF_INET || + sc->gif_pdst->sa_family != AF_INET) + return 0; + return gif_encapcheck4(m, off, proto, arg); +#endif +#ifdef INET6 + case 6: + if (sc->gif_psrc->sa_family != AF_INET6 || + sc->gif_pdst->sa_family != AF_INET6) + return 0; + return gif_encapcheck6(m, off, proto, arg); +#endif + default: + return 0; + } +} + int gif_output(ifp, m, dst, rt) struct ifnet *ifp; struct mbuf *m; struct sockaddr *dst; struct rtentry *rt; /* added in net2 */ { register struct gif_softc *sc = (struct gif_softc*)ifp; int error = 0; static int called = 0; /* XXX: MUTEX */ - int calllimit = 10; /* XXX: adhoc */ /* * gif may cause infinite recursion calls when misconfigured. * We'll prevent this by introducing upper limit. * XXX: this mechanism may introduce another problem about * mutual exclusion of the variable CALLED, especially if we * use kernel thread. */ - if (++called >= calllimit) { + if (++called > max_gif_nesting) { log(LOG_NOTICE, "gif_output: recursively called too many times(%d)\n", called); m_freem(m); error = EIO; /* is there better errno? */ goto end; } + getmicrotime(&ifp->if_lastchange); m->m_flags &= ~(M_BCAST|M_MCAST); if (!(ifp->if_flags & IFF_UP) || sc->gif_psrc == NULL || sc->gif_pdst == NULL) { m_freem(m); error = ENETDOWN; goto end; } +#if NBPFILTER > 0 if (ifp->if_bpf) { /* * We need to prepend the address family as * a four byte field. Cons up a dummy header * to pacify bpf. This is safe because bpf * will only read from the mbuf (i.e., it won't * try to free it or keep a pointer a to it). */ struct mbuf m0; u_int af = dst->sa_family; m0.m_next = m; m0.m_len = 4; m0.m_data = (char *)⁡ - + +#ifdef HAVE_OLD_BPF bpf_mtap(ifp, &m0); +#else + bpf_mtap(ifp->if_bpf, &m0); +#endif } - ifp->if_opackets++; +#endif + ifp->if_opackets++; ifp->if_obytes += m->m_pkthdr.len; + /* XXX should we check if our outer source is legal? */ + switch (sc->gif_psrc->sa_family) { #ifdef INET case AF_INET: error = in_gif_output(ifp, dst->sa_family, m, rt); break; #endif #ifdef INET6 case AF_INET6: error = in6_gif_output(ifp, dst->sa_family, m, rt); break; #endif default: - m_freem(m); + m_freem(m); error = ENETDOWN; } end: called = 0; /* reset recursion counter */ if (error) ifp->if_oerrors++; return error; } void gif_input(m, af, gifp) struct mbuf *m; int af; struct ifnet *gifp; { int s, isr; register struct ifqueue *ifq = 0; if (gifp == NULL) { /* just in case */ m_freem(m); return; } - if (m->m_pkthdr.rcvif) - m->m_pkthdr.rcvif = gifp; - + m->m_pkthdr.rcvif = gifp; + +#if NBPFILTER > 0 if (gifp->if_bpf) { /* * We need to prepend the address family as * a four byte field. Cons up a dummy header * to pacify bpf. This is safe because bpf * will only read from the mbuf (i.e., it won't * try to free it or keep a pointer a to it). */ struct mbuf m0; u_int af = AF_INET6; - + m0.m_next = m; m0.m_len = 4; m0.m_data = (char *)⁡ - + +#ifdef HAVE_OLD_BPF bpf_mtap(gifp, &m0); +#else + bpf_mtap(gifp->if_bpf, &m0); +#endif } +#endif /*NBPFILTER > 0*/ /* * Put the packet to the network layer input queue according to the * specified address family. * Note: older versions of gif_input directly called network layer * input functions, e.g. ip6_input, here. We changed the policy to * prevent too many recursive calls of such input functions, which * might cause kernel panic. But the change may introduce another * problem; if the input queue is full, packets are discarded. * We believed it rarely occurs and changed the policy. If we find * it occurs more times than we thought, we may change the policy * again. */ switch (af) { #ifdef INET case AF_INET: ifq = &ipintrq; isr = NETISR_IP; break; #endif #ifdef INET6 case AF_INET6: ifq = &ip6intrq; isr = NETISR_IPV6; break; #endif default: m_freem(m); return; } s = splimp(); if (IF_QFULL(ifq)) { IF_DROP(ifq); /* update statistics */ m_freem(m); splx(s); return; } IF_ENQUEUE(ifq, m); /* we need schednetisr since the address family may change */ schednetisr(isr); gifp->if_ipackets++; gifp->if_ibytes += m->m_pkthdr.len; splx(s); return; } - +/* XXX how should we handle IPv6 scope on SIOC[GS]IFPHYADDR? */ int gif_ioctl(ifp, cmd, data) struct ifnet *ifp; u_long cmd; caddr_t data; { struct gif_softc *sc = (struct gif_softc*)ifp; struct ifreq *ifr = (struct ifreq*)data; int error = 0, size; - struct sockaddr *sa, *dst, *src; - + struct sockaddr *dst, *src; + struct sockaddr *sa; + int i; + struct gif_softc *sc2; + switch (cmd) { case SIOCSIFADDR: break; - + case SIOCSIFDSTADDR: break; case SIOCADDMULTI: case SIOCDELMULTI: break; +#ifdef SIOCSIFMTU /* xxx */ case SIOCGIFMTU: break; + case SIOCSIFMTU: { u_long mtu; mtu = ifr->ifr_mtu; if (mtu < GIF_MTU_MIN || mtu > GIF_MTU_MAX) { return (EINVAL); } ifp->if_mtu = mtu; } break; +#endif /* SIOCSIFMTU */ case SIOCSIFPHYADDR: #ifdef INET6 case SIOCSIFPHYADDR_IN6: #endif /* INET6 */ - switch (ifr->ifr_addr.sa_family) { -#ifdef INET - case AF_INET: + switch (cmd) { + case SIOCSIFPHYADDR: src = (struct sockaddr *) &(((struct in_aliasreq *)data)->ifra_addr); dst = (struct sockaddr *) &(((struct in_aliasreq *)data)->ifra_dstaddr); - - /* only one gif can have dst = INADDR_ANY */ -#define satosaddr(sa) (((struct sockaddr_in *)(sa))->sin_addr.s_addr) - -#ifdef INET6 - if (bcmp(ifp->if_name, "stf", 3) == 0) - satosaddr(dst) = INADDR_BROADCAST; -#endif - - if (satosaddr(dst) == INADDR_ANY) { - int i; - struct gif_softc *sc2; - - for (i = 0, sc2 = gif; i < ngif; i++, sc2++) { - if (sc2 == sc) continue; - if (sc2->gif_pdst && - satosaddr(sc2->gif_pdst) - == INADDR_ANY) { - error = EADDRNOTAVAIL; - goto bad; - } - } - } - size = sizeof(struct sockaddr_in); break; -#endif /* INET */ #ifdef INET6 - case AF_INET6: + case SIOCSIFPHYADDR_IN6: src = (struct sockaddr *) &(((struct in6_aliasreq *)data)->ifra_addr); dst = (struct sockaddr *) &(((struct in6_aliasreq *)data)->ifra_dstaddr); + break; +#endif + } - /* only one gif can have dst = in6addr_any */ -#define satoin6(sa) (&((struct sockaddr_in6 *)(sa))->sin6_addr) + for (i = 0; i < ngif; i++) { + sc2 = gif + i; + if (sc2 == sc) + continue; + if (!sc2->gif_pdst || !sc2->gif_psrc) + continue; + if (sc2->gif_pdst->sa_family != dst->sa_family || + sc2->gif_pdst->sa_len != dst->sa_len || + sc2->gif_psrc->sa_family != src->sa_family || + sc2->gif_psrc->sa_len != src->sa_len) + continue; + /* can't configure same pair of address onto two gifs */ + if (bcmp(sc2->gif_pdst, dst, dst->sa_len) == 0 && + bcmp(sc2->gif_psrc, src, src->sa_len) == 0) { + error = EADDRNOTAVAIL; + goto bad; + } - if (IN6_IS_ADDR_UNSPECIFIED(satoin6(dst))) { - int i; - struct gif_softc *sc2; - - for (i = 0, sc2 = gif; i < ngif; i++, sc2++) { - if (sc2 == sc) continue; - if (sc2->gif_pdst && - IN6_IS_ADDR_UNSPECIFIED( - satoin6(sc2->gif_pdst) - )) { - error = EADDRNOTAVAIL; - goto bad; - } - } + /* can't configure multiple multi-dest interfaces */ +#define multidest(x) \ + (((struct sockaddr_in *)(x))->sin_addr.s_addr == INADDR_ANY) +#ifdef INET6 +#define multidest6(x) \ + (IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6 *)(x))->sin6_addr)) +#endif + if (dst->sa_family == AF_INET && + multidest(dst) && multidest(sc2->gif_pdst)) { + error = EADDRNOTAVAIL; + goto bad; } +#ifdef INET6 + if (dst->sa_family == AF_INET6 && + multidest6(dst) && multidest6(sc2->gif_pdst)) { + error = EADDRNOTAVAIL; + goto bad; + } +#endif + } + + if (src->sa_family != dst->sa_family || + src->sa_len != dst->sa_len) { + error = EINVAL; + break; + } + switch (src->sa_family) { +#ifdef INET + case AF_INET: + size = sizeof(struct sockaddr_in); + break; +#endif +#ifdef INET6 + case AF_INET6: size = sizeof(struct sockaddr_in6); break; -#endif /* INET6 */ +#endif default: - error = EPROTOTYPE; + error = EAFNOSUPPORT; goto bad; + } + if (src->sa_len != size) { + error = EINVAL; break; } - if (sc->gif_psrc != NULL) - free((caddr_t)sc->gif_psrc, M_IFADDR); - if (sc->gif_pdst != NULL) - free((caddr_t)sc->gif_pdst, M_IFADDR); + if (sc->gif_psrc) + free((caddr_t)sc->gif_psrc, M_IFADDR); sa = (struct sockaddr *)malloc(size, M_IFADDR, M_WAITOK); - bzero((caddr_t)sa, size); bcopy((caddr_t)src, (caddr_t)sa, size); sc->gif_psrc = sa; + if (sc->gif_pdst) + free((caddr_t)sc->gif_pdst, M_IFADDR); sa = (struct sockaddr *)malloc(size, M_IFADDR, M_WAITOK); - bzero((caddr_t)sa, size); bcopy((caddr_t)dst, (caddr_t)sa, size); sc->gif_pdst = sa; - ifp->if_flags |= (IFF_UP|IFF_RUNNING); - { - int s; + ifp->if_flags |= IFF_UP; + if_up(ifp); /* send up RTM_IFINFO */ - s = splnet(); - if_up(ifp); /* send up RTM_IFINFO */ - splx(s); - } - + error = 0; break; +#ifdef SIOCDIFPHYADDR + case SIOCDIFPHYADDR: + if (sc->gif_psrc) { + free((caddr_t)sc->gif_psrc, M_IFADDR); + sc->gif_psrc = NULL; + } + if (sc->gif_pdst) { + free((caddr_t)sc->gif_pdst, M_IFADDR); + sc->gif_pdst = NULL; + } + /* change the IFF_UP flag as well? */ + break; +#endif + case SIOCGIFPSRCADDR: #ifdef INET6 case SIOCGIFPSRCADDR_IN6: #endif /* INET6 */ if (sc->gif_psrc == NULL) { error = EADDRNOTAVAIL; goto bad; } src = sc->gif_psrc; switch (sc->gif_psrc->sa_family) { #ifdef INET case AF_INET: dst = &ifr->ifr_addr; size = sizeof(struct sockaddr_in); break; #endif /* INET */ #ifdef INET6 case AF_INET6: dst = (struct sockaddr *) &(((struct in6_ifreq *)data)->ifr_addr); size = sizeof(struct sockaddr_in6); break; #endif /* INET6 */ default: error = EADDRNOTAVAIL; goto bad; } bcopy((caddr_t)src, (caddr_t)dst, size); break; - + case SIOCGIFPDSTADDR: #ifdef INET6 case SIOCGIFPDSTADDR_IN6: #endif /* INET6 */ if (sc->gif_pdst == NULL) { error = EADDRNOTAVAIL; goto bad; } src = sc->gif_pdst; switch (sc->gif_pdst->sa_family) { #ifdef INET case AF_INET: dst = &ifr->ifr_addr; size = sizeof(struct sockaddr_in); break; #endif /* INET */ #ifdef INET6 case AF_INET6: dst = (struct sockaddr *) &(((struct in6_ifreq *)data)->ifr_addr); size = sizeof(struct sockaddr_in6); break; #endif /* INET6 */ default: error = EADDRNOTAVAIL; goto bad; } bcopy((caddr_t)src, (caddr_t)dst, size); break; case SIOCSIFFLAGS: + /* if_ioctl() takes care of it */ break; default: error = EINVAL; break; } bad: return error; } +#endif /*NGIF > 0*/ Index: head/sys/net/if_gif.h =================================================================== --- head/sys/net/if_gif.h (revision 62586) +++ head/sys/net/if_gif.h (revision 62587) @@ -1,68 +1,82 @@ +/* $FreeBSD$ */ +/* $KAME: if_gif.h,v 1.13 2000/06/17 20:34:24 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* * if_gif.h */ #ifndef _NET_IF_GIF_H_ #define _NET_IF_GIF_H_ + +#if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined(__NetBSD__) +#if defined(_KERNEL) && !defined(_LKM) +#include "opt_inet.h" +#endif +#endif + +#include +/* xxx sigh, why route have struct route instead of pointer? */ + +struct encaptab; + struct gif_softc { - struct ifnet gif_if; /* common area */ - struct sockaddr *gif_psrc; /* Physical src addr */ - struct sockaddr *gif_pdst; /* Physical dst addr */ + struct ifnet gif_if; /* common area - must be at the top */ + struct sockaddr *gif_psrc; /* Physical src addr */ + struct sockaddr *gif_pdst; /* Physical dst addr */ union { - struct route gifscr_ro; /* xxx */ - struct route_in6 gifscr_ro6; /* xxx */ + struct route gifscr_ro; /* xxx */ +#ifdef INET6 + struct route_in6 gifscr_ro6; /* xxx */ +#endif } gifsc_gifscr; - int gif_flags; + int gif_flags; + const struct encaptab *encap_cookie4; + const struct encaptab *encap_cookie6; }; -#define gif_ro gifsc_gifscr.gifscr_ro -#define gif_ro6 gifsc_gifscr.gifscr_ro6 +#define gif_ro gifsc_gifscr.gifscr_ro +#ifdef INET6 +#define gif_ro6 gifsc_gifscr.gifscr_ro6 +#endif -#define GIFF_INUSE 0x1 /* gif is in use */ - -#define GIF_MTU (1280) /* Default MTU */ +#define GIF_MTU (1280) /* Default MTU */ #define GIF_MTU_MIN (1280) /* Minimum MTU */ #define GIF_MTU_MAX (8192) /* Maximum MTU */ -extern int ngif; -extern struct gif_softc *gif; - /* Prototypes */ -void gif_input __P((struct mbuf *, int, struct ifnet *)); -int gif_output __P((struct ifnet *, struct mbuf *, +void gif_input __P((struct mbuf *, int, struct ifnet *)); +int gif_output __P((struct ifnet *, struct mbuf *, struct sockaddr *, struct rtentry *)); -int gif_ioctl __P((struct ifnet *, u_long, caddr_t)); +int gif_ioctl __P((struct ifnet *, u_long, caddr_t)); #endif /* _NET_IF_GIF_H_ */ Index: head/sys/net/if_loop.c =================================================================== --- head/sys/net/if_loop.c (revision 62586) +++ head/sys/net/if_loop.c (revision 62587) @@ -1,391 +1,391 @@ /* * Copyright (c) 1982, 1986, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)if_loop.c 8.1 (Berkeley) 6/10/93 * $FreeBSD$ */ /* * Loopback interface driver for protocol testing and timing. */ #include "loop.h" #include "opt_atalk.h" #include "opt_inet.h" #include "opt_inet6.h" #include "opt_ipx.h" #include #include #include #include #include #include #include #include #include #include #include #ifdef INET #include #include #endif #ifdef IPX #include #include #endif #ifdef INET6 #ifndef INET #include #endif #include -#include +#include #endif #ifdef NS #include #include #endif #ifdef NETATALK #include #include #endif NETATALK int loioctl __P((struct ifnet *, u_long, caddr_t)); static void lortrequest __P((int, struct rtentry *, struct sockaddr *)); static void loopattach __P((void *)); PSEUDO_SET(loopattach, if_loop); int looutput __P((struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, struct rtentry *rt)); #ifdef TINY_LOMTU #define LOMTU (1024+512) #elif defined(LARGE_LOMTU) #define LOMTU 131072 #else #define LOMTU 16384 #endif struct ifnet loif[NLOOP]; /* ARGSUSED */ static void loopattach(dummy) void *dummy; { register struct ifnet *ifp; register int i = 0; for (ifp = loif; i < NLOOP; ifp++) { ifp->if_name = "lo"; ifp->if_unit = i++; ifp->if_mtu = LOMTU; ifp->if_flags = IFF_LOOPBACK | IFF_MULTICAST; ifp->if_ioctl = loioctl; ifp->if_output = looutput; ifp->if_type = IFT_LOOP; ifp->if_snd.ifq_maxlen = ifqmaxlen; if_attach(ifp); bpfattach(ifp, DLT_NULL, sizeof(u_int)); } } int looutput(ifp, m, dst, rt) struct ifnet *ifp; register struct mbuf *m; struct sockaddr *dst; register struct rtentry *rt; { if ((m->m_flags & M_PKTHDR) == 0) panic("looutput no HDR"); if (rt && rt->rt_flags & (RTF_REJECT|RTF_BLACKHOLE)) { m_freem(m); return (rt->rt_flags & RTF_BLACKHOLE ? 0 : rt->rt_flags & RTF_HOST ? EHOSTUNREACH : ENETUNREACH); } /* * KAME requires that the packet to be contiguous on the * mbuf. We need to make that sure. * this kind of code should be avoided. * XXX: fails to join if interface MTU > MCLBYTES. jumbogram? */ if (m && m->m_next != NULL && m->m_pkthdr.len < MCLBYTES) { struct mbuf *n; MGETHDR(n, M_DONTWAIT, MT_HEADER); if (!n) goto contiguousfail; MCLGET(n, M_DONTWAIT); if (! (n->m_flags & M_EXT)) { m_freem(n); goto contiguousfail; } m_copydata(m, 0, m->m_pkthdr.len, mtod(n, caddr_t)); n->m_pkthdr = m->m_pkthdr; n->m_len = m->m_pkthdr.len; m_freem(m); m = n; } if (0) { contiguousfail: printf("looutput: mbuf allocation failed\n"); } ifp->if_opackets++; ifp->if_obytes += m->m_pkthdr.len; #if 1 /* XXX */ switch (dst->sa_family) { case AF_INET: case AF_INET6: case AF_IPX: case AF_NS: case AF_APPLETALK: break; default: printf("looutput: af=%d unexpected", dst->sa_family); m_freem(m); return (EAFNOSUPPORT); } #endif return(if_simloop(ifp, m, dst->sa_family, 0)); } /* * if_simloop() * * This function is to support software emulation of hardware loopback, * i.e., for interfaces with the IFF_SIMPLEX attribute. Since they can't * hear their own broadcasts, we create a copy of the packet that we * would normally receive via a hardware loopback. * * This function expects the packet to include the media header of length hlen. */ int if_simloop(ifp, m, af, hlen) struct ifnet *ifp; register struct mbuf *m; int af; int hlen; { int s, isr; register struct ifqueue *ifq = 0; KASSERT((m->m_flags & M_PKTHDR) != 0, ("if_simloop: no HDR")); m->m_pkthdr.rcvif = ifp; /* BPF write needs to be handled specially */ if (af == AF_UNSPEC) { KASSERT(m->m_len >= sizeof(int), ("if_simloop: m_len")); af = *(mtod(m, int *)); m->m_len -= sizeof(int); m->m_pkthdr.len -= sizeof(int); m->m_data += sizeof(int); } /* Let BPF see incoming packet */ if (ifp->if_bpf) { struct mbuf m0, *n = m; /* * We need to prepend the address family as * a four byte field. Cons up a dummy header * to pacify bpf. This is safe because bpf * will only read from the mbuf (i.e., it won't * try to free it or keep a pointer a to it). */ m0.m_next = m; m0.m_len = 4; m0.m_data = (char *)⁡ n = &m0; bpf_mtap(ifp, n); } /* Strip away media header */ if (hlen > 0) { m_adj(m, hlen); #ifdef __alpha__ /* The alpha doesn't like unaligned data. * We move data down in the first mbuf */ if (mtod(m, vm_offset_t) & 3) { KASSERT(hlen >= 3, ("if_simloop: hlen too small")); bcopy(m->m_data, (char *)(mtod(m, vm_offset_t) - (mtod(m, vm_offset_t) & 3)), m->m_len); mtod(m,vm_offset_t) -= (mtod(m, vm_offset_t) & 3); } #endif } /* Deliver to upper layer protocol */ switch (af) { #ifdef INET case AF_INET: ifq = &ipintrq; isr = NETISR_IP; break; #endif #ifdef INET6 case AF_INET6: m->m_flags |= M_LOOP; ifq = &ip6intrq; isr = NETISR_IPV6; break; #endif #ifdef IPX case AF_IPX: ifq = &ipxintrq; isr = NETISR_IPX; break; #endif #ifdef NS case AF_NS: ifq = &nsintrq; isr = NETISR_NS; break; #endif #ifdef NETATALK case AF_APPLETALK: ifq = &atintrq2; isr = NETISR_ATALK; break; #endif NETATALK default: printf("if_simloop: can't handle af=%d\n", af); m_freem(m); return (EAFNOSUPPORT); } s = splimp(); if (IF_QFULL(ifq)) { IF_DROP(ifq); m_freem(m); splx(s); return (ENOBUFS); } IF_ENQUEUE(ifq, m); schednetisr(isr); ifp->if_ipackets++; ifp->if_ibytes += m->m_pkthdr.len; splx(s); return (0); } /* ARGSUSED */ static void lortrequest(cmd, rt, sa) int cmd; struct rtentry *rt; struct sockaddr *sa; { if (rt) { rt->rt_rmx.rmx_mtu = rt->rt_ifp->if_mtu; /* for ISO */ /* * For optimal performance, the send and receive buffers * should be at least twice the MTU plus a little more for * overhead. */ rt->rt_rmx.rmx_recvpipe = rt->rt_rmx.rmx_sendpipe = 3 * LOMTU; } } /* * Process an ioctl request. */ /* ARGSUSED */ int loioctl(ifp, cmd, data) register struct ifnet *ifp; u_long cmd; caddr_t data; { register struct ifaddr *ifa; register struct ifreq *ifr = (struct ifreq *)data; register int error = 0; switch (cmd) { case SIOCSIFADDR: ifp->if_flags |= IFF_UP | IFF_RUNNING; ifa = (struct ifaddr *)data; ifa->ifa_rtrequest = lortrequest; /* * Everything else is done at a higher level. */ break; case SIOCADDMULTI: case SIOCDELMULTI: if (ifr == 0) { error = EAFNOSUPPORT; /* XXX */ break; } switch (ifr->ifr_addr.sa_family) { #ifdef INET case AF_INET: break; #endif #ifdef INET6 case AF_INET6: break; #endif default: error = EAFNOSUPPORT; break; } break; case SIOCSIFMTU: ifp->if_mtu = ifr->ifr_mtu; break; case SIOCSIFFLAGS: break; default: error = EINVAL; } return (error); } Index: head/sys/net/if_stf.c =================================================================== --- head/sys/net/if_stf.c (nonexistent) +++ head/sys/net/if_stf.c (revision 62587) @@ -0,0 +1,662 @@ +/* $FreeBSD$ */ +/* $KAME: if_stf.c,v 1.40 2000/06/20 19:44:42 itojun Exp $ */ + +/* + * Copyright (C) 2000 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * 6to4 interface, based on draft-ietf-ngtrans-6to4-06.txt. + * + * 6to4 interface is NOT capable of link-layer (I mean, IPv4) multicasting. + * There is no address mapping defined from IPv6 multicast address to IPv4 + * address. Therefore, we do not have IFF_MULTICAST on the interface. + * + * Due to the lack of address mapping for link-local addresses, we cannot + * throw packets toward link-local addresses (fe80::x). Also, we cannot throw + * packets to link-local multicast addresses (ff02::x). + * + * Here are interesting symptoms due to the lack of link-local address: + * + * Unicast routing exchange: + * - RIPng: Impossible. Uses link-local multicast packet toward ff02::9, + * and link-local addresses as nexthop. + * - OSPFv6: Impossible. OSPFv6 assumes that there's link-local address + * assigned to the link, and makes use of them. Also, HELLO packets use + * link-local multicast addresses (ff02::5 and ff02::6). + * - BGP4+: Maybe. You can only use global address as nexthop, and global + * address as TCP endpoint address. + * + * Multicast routing protocols: + * - PIM: Hello packet cannot be used to discover adjacent PIM routers. + * Adjacent PIM routers must be configured manually (is it really spec-wise + * correct thing to do?). + * + * ICMPv6: + * - Redirects cannot be used due to the lack of link-local address. + * + * Starting from 04 draft, the specification suggests how to construct + * link-local address for 6to4 interface. + * However, it seems to have no real use and does not help the above symptom + * much. Even if we assign link-locals to interface, we cannot really + * use link-local unicast/multicast on top of 6to4 cloud, and the above + * analysis does not change. + * + * 6to4 interface has security issues. Refer to + * http://playground.iijlab.net/i-d/draft-itojun-ipv6-transition-abuse-00.txt + * for details. The code tries to filter out some of malicious packets. + * Note that there is no way to be 100% secure. + */ + +#include "opt_inet.h" +#include "opt_inet6.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include "bpf.h" +#define NBPFILTER NBPF +#include "stf.h" +#include "gif.h" /*XXX*/ + +#if NBPFILTER > 0 +#include +#endif + +#if NGIF > 0 +#include +#endif + +#if NSTF > 0 +#if NSTF != 1 +# error only single stf interface allowed +#endif + +#define IN6_IS_ADDR_6TO4(x) (ntohs((x)->s6_addr16[0]) == 0x2002) +#define GET_V4(x) ((struct in_addr *)(&(x)->s6_addr16[1])) + +struct stf_softc { + struct ifnet sc_if; /* common area */ + union { + struct route __sc_ro4; + struct route_in6 __sc_ro6; /* just for safety */ + } __sc_ro46; +#define sc_ro __sc_ro46.__sc_ro4 + const struct encaptab *encap_cookie; +}; + +static struct stf_softc *stf; +static int nstf; + +#if NGIF > 0 +extern int ip_gif_ttl; /*XXX*/ +#else +static int ip_gif_ttl = 40; /*XXX*/ +#endif + +extern struct protosw in_stf_protosw; + +void stfattach __P((void *)); +static int stf_encapcheck __P((const struct mbuf *, int, int, void *)); +static struct in6_ifaddr *stf_getsrcifa6 __P((struct ifnet *)); +static int stf_output __P((struct ifnet *, struct mbuf *, struct sockaddr *, + struct rtentry *)); +static int stf_checkaddr4 __P((struct in_addr *, struct ifnet *)); +static int stf_checkaddr6 __P((struct in6_addr *, struct ifnet *)); +static void stf_rtrequest __P((int, struct rtentry *, struct sockaddr *)); +static int stf_ioctl __P((struct ifnet *, u_long, caddr_t)); + +void +stfattach(dummy) + void *dummy; +{ + struct stf_softc *sc; + int i; + const struct encaptab *p; + + nstf = NSTF; + stf = malloc(nstf * sizeof(struct stf_softc), M_DEVBUF, M_WAIT); + bzero(stf, nstf * sizeof(struct stf_softc)); + sc = stf; + + /* XXX just in case... */ + for (i = 0; i < nstf; i++) { + sc = &stf[i]; + bzero(sc, sizeof(*sc)); + sc->sc_if.if_name = "stf"; + sc->sc_if.if_unit = i; + + p = encap_attach_func(AF_INET, IPPROTO_IPV6, stf_encapcheck, + &in_stf_protosw, sc); + if (p == NULL) { + printf("%s: attach failed\n", if_name(&sc->sc_if)); + continue; + } + sc->encap_cookie = p; + + sc->sc_if.if_mtu = IPV6_MMTU; + sc->sc_if.if_flags = 0; + sc->sc_if.if_ioctl = stf_ioctl; + sc->sc_if.if_output = stf_output; + sc->sc_if.if_type = IFT_STF; + sc->sc_if.if_snd.ifq_maxlen = IFQ_MAXLEN; + if_attach(&sc->sc_if); +#if NBPFILTER > 0 +#ifdef HAVE_OLD_BPF + bpfattach(&sc->sc_if, DLT_NULL, sizeof(u_int)); +#else + bpfattach(&sc->sc_if.if_bpf, &sc->sc_if, DLT_NULL, sizeof(u_int)); +#endif +#endif + } +} + +PSEUDO_SET(stfattach, if_stf); + +static int +stf_encapcheck(m, off, proto, arg) + const struct mbuf *m; + int off; + int proto; + void *arg; +{ + struct ip ip; + struct in6_ifaddr *ia6; + struct stf_softc *sc; + struct in_addr a, b; + + sc = (struct stf_softc *)arg; + if (sc == NULL) + return 0; + + if ((sc->sc_if.if_flags & IFF_UP) == 0) + return 0; + + if (proto != IPPROTO_IPV6) + return 0; + + /* LINTED const cast */ + m_copydata((struct mbuf *)m, 0, sizeof(ip), (caddr_t)&ip); + + if (ip.ip_v != 4) + return 0; + + ia6 = stf_getsrcifa6(&sc->sc_if); + if (ia6 == NULL) + return 0; + + /* + * check if IPv4 dst matches the IPv4 address derived from the + * local 6to4 address. + * success on: dst = 10.1.1.1, ia6->ia_addr = 2002:0a01:0101:... + */ + if (bcmp(GET_V4(&ia6->ia_addr.sin6_addr), &ip.ip_dst, + sizeof(ip.ip_dst)) != 0) + return 0; + + /* + * check if IPv4 src matches the IPv4 address derived from the + * local 6to4 address masked by prefixmask. + * success on: src = 10.1.1.1, ia6->ia_addr = 2002:0a00:.../24 + * fail on: src = 10.1.1.1, ia6->ia_addr = 2002:0b00:.../24 + */ + bzero(&a, sizeof(a)); + a.s_addr = GET_V4(&ia6->ia_addr.sin6_addr)->s_addr; + a.s_addr &= GET_V4(&ia6->ia_prefixmask.sin6_addr)->s_addr; + b = ip.ip_src; + b.s_addr &= GET_V4(&ia6->ia_prefixmask.sin6_addr)->s_addr; + if (a.s_addr != b.s_addr) + return 0; + + /* stf interface makes single side match only */ + return 32; +} + +static struct in6_ifaddr * +stf_getsrcifa6(ifp) + struct ifnet *ifp; +{ + struct ifaddr *ia; + struct in_ifaddr *ia4; + struct sockaddr_in6 *sin6; + struct in_addr in; + + for (ia = ifp->if_addrlist.tqh_first; + ia; + ia = ia->ifa_list.tqe_next) + { + if (ia->ifa_addr == NULL) + continue; + if (ia->ifa_addr->sa_family != AF_INET6) + continue; + sin6 = (struct sockaddr_in6 *)ia->ifa_addr; + if (!IN6_IS_ADDR_6TO4(&sin6->sin6_addr)) + continue; + + bcopy(GET_V4(&sin6->sin6_addr), &in, sizeof(in)); + for (ia4 = TAILQ_FIRST(&in_ifaddrhead); + ia4; + ia4 = TAILQ_NEXT(ia4, ia_link)) + { + if (ia4->ia_addr.sin_addr.s_addr == in.s_addr) + break; + } + if (ia4 == NULL) + continue; + + return (struct in6_ifaddr *)ia; + } + + return NULL; +} + +#ifndef offsetof +#define offsetof(s, e) ((int)&((s *)0)->e) +#endif + +static int +stf_output(ifp, m, dst, rt) + struct ifnet *ifp; + struct mbuf *m; + struct sockaddr *dst; + struct rtentry *rt; +{ + struct stf_softc *sc; + struct sockaddr_in6 *dst6; + struct sockaddr_in *dst4; + u_int8_t tos; + struct ip *ip; + struct ip6_hdr *ip6; + struct in6_ifaddr *ia6; + + sc = (struct stf_softc*)ifp; + dst6 = (struct sockaddr_in6 *)dst; + + /* just in case */ + if ((ifp->if_flags & IFF_UP) == 0) { + m_freem(m); + return ENETDOWN; + } + + /* + * If we don't have an ip4 address that match my inner ip6 address, + * we shouldn't generate output. Without this check, we'll end up + * using wrong IPv4 source. + */ + ia6 = stf_getsrcifa6(ifp); + if (ia6 == NULL) { + m_freem(m); + return ENETDOWN; + } + + if (m->m_len < sizeof(*ip6)) { + m = m_pullup(m, sizeof(*ip6)); + if (!m) + return ENOBUFS; + } + ip6 = mtod(m, struct ip6_hdr *); + tos = (ntohl(ip6->ip6_flow) >> 20) & 0xff; + + M_PREPEND(m, sizeof(struct ip), M_DONTWAIT); + if (m && m->m_len < sizeof(struct ip)) + m = m_pullup(m, sizeof(struct ip)); + if (m == NULL) + return ENOBUFS; + ip = mtod(m, struct ip *); + + bzero(ip, sizeof(*ip)); + + bcopy(GET_V4(&((struct sockaddr_in6 *)&ia6->ia_addr)->sin6_addr), + &ip->ip_src, sizeof(ip->ip_src)); + bcopy(GET_V4(&dst6->sin6_addr), &ip->ip_dst, sizeof(ip->ip_dst)); + ip->ip_p = IPPROTO_IPV6; + ip->ip_ttl = ip_gif_ttl; /*XXX*/ + ip->ip_len = m->m_pkthdr.len; /*host order*/ + if (ifp->if_flags & IFF_LINK1) + ip_ecn_ingress(ECN_ALLOWED, &ip->ip_tos, &tos); + + dst4 = (struct sockaddr_in *)&sc->sc_ro.ro_dst; + if (dst4->sin_family != AF_INET || + bcmp(&dst4->sin_addr, &ip->ip_dst, sizeof(ip->ip_dst)) != 0) { + /* cache route doesn't match */ + dst4->sin_family = AF_INET; + dst4->sin_len = sizeof(struct sockaddr_in); + bcopy(&ip->ip_dst, &dst4->sin_addr, sizeof(dst4->sin_addr)); + if (sc->sc_ro.ro_rt) { + RTFREE(sc->sc_ro.ro_rt); + sc->sc_ro.ro_rt = NULL; + } + } + + if (sc->sc_ro.ro_rt == NULL) { + rtalloc(&sc->sc_ro); + if (sc->sc_ro.ro_rt == NULL) { + m_freem(m); + return ENETUNREACH; + } + } + + return ip_output(m, NULL, &sc->sc_ro, 0, NULL); +} + +static int +stf_checkaddr4(in, ifp) + struct in_addr *in; + struct ifnet *ifp; /* incoming interface */ +{ + struct in_ifaddr *ia4; + + /* + * reject packets with the following address: + * 224.0.0.0/4 0.0.0.0/8 127.0.0.0/8 255.0.0.0/8 + */ + if (IN_MULTICAST(in->s_addr)) + return -1; + switch ((ntohl(in->s_addr) & 0xff000000) >> 24) { + case 0: case 127: case 255: + return -1; + } + + /* + * reject packets with broadcast + */ + for (ia4 = TAILQ_FIRST(&in_ifaddrhead); + ia4; + ia4 = TAILQ_NEXT(ia4, ia_link)) + { + if ((ia4->ia_ifa.ifa_ifp->if_flags & IFF_BROADCAST) == 0) + continue; + if (in->s_addr == ia4->ia_broadaddr.sin_addr.s_addr) + return -1; + } + + /* + * perform ingress filter + */ + if (ifp) { + struct sockaddr_in sin; + struct rtentry *rt; + + bzero(&sin, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_len = sizeof(struct sockaddr_in); + sin.sin_addr = *in; + rt = rtalloc1((struct sockaddr *)&sin, 0, 0UL); + if (!rt) + return -1; + if (rt->rt_ifp != ifp) { + rtfree(rt); + return -1; + } + rtfree(rt); + } + + return 0; +} + +static int +stf_checkaddr6(in6, ifp) + struct in6_addr *in6; + struct ifnet *ifp; /* incoming interface */ +{ + /* + * check 6to4 addresses + */ + if (IN6_IS_ADDR_6TO4(in6)) + return stf_checkaddr4(GET_V4(in6), ifp); + + /* + * reject anything that look suspicious. the test is implemented + * in ip6_input too, but we check here as well to + * (1) reject bad packets earlier, and + * (2) to be safe against future ip6_input change. + */ + if (IN6_IS_ADDR_V4COMPAT(in6) || IN6_IS_ADDR_V4MAPPED(in6)) + return -1; + + return 0; +} + +void +#if __STDC__ +in_stf_input(struct mbuf *m, ...) +#else +in_stf_input(m, va_alist) + register struct mbuf *m; +#endif +{ + int off, proto; + struct stf_softc *sc; + struct ip *ip; + struct ip6_hdr *ip6; + u_int8_t otos, itos; + int s, isr; + struct ifqueue *ifq = NULL; + struct ifnet *ifp; + va_list ap; + + va_start(ap, m); + off = va_arg(ap, int); + proto = va_arg(ap, int); + va_end(ap); + + if (proto != IPPROTO_IPV6) { + m_freem(m); + return; + } + + ip = mtod(m, struct ip *); + + sc = (struct stf_softc *)encap_getarg(m); + + if (sc == NULL || (sc->sc_if.if_flags & IFF_UP) == 0) { + m_freem(m); + return; + } + + ifp = &sc->sc_if; + + /* + * perform sanity check against outer src/dst. + * for source, perform ingress filter as well. + */ + if (stf_checkaddr4(&ip->ip_dst, NULL) < 0 || + stf_checkaddr4(&ip->ip_src, m->m_pkthdr.rcvif) < 0) { + m_freem(m); + return; + } + + otos = ip->ip_tos; + m_adj(m, off); + + if (m->m_len < sizeof(*ip6)) { + m = m_pullup(m, sizeof(*ip6)); + if (!m) + return; + } + ip6 = mtod(m, struct ip6_hdr *); + + /* + * perform sanity check against inner src/dst. + * for source, perform ingress filter as well. + */ + if (stf_checkaddr6(&ip6->ip6_dst, NULL) < 0 || + stf_checkaddr6(&ip6->ip6_src, m->m_pkthdr.rcvif) < 0) { + m_freem(m); + return; + } + + itos = (ntohl(ip6->ip6_flow) >> 20) & 0xff; + if ((ifp->if_flags & IFF_LINK1) != 0) + ip_ecn_egress(ECN_ALLOWED, &otos, &itos); + ip6->ip6_flow &= ~htonl(0xff << 20); + ip6->ip6_flow |= htonl((u_int32_t)itos << 20); + + m->m_pkthdr.rcvif = ifp; + +#if NBPFILTER > 0 + if (ifp->if_bpf) { + /* + * We need to prepend the address family as + * a four byte field. Cons up a dummy header + * to pacify bpf. This is safe because bpf + * will only read from the mbuf (i.e., it won't + * try to free it or keep a pointer a to it). + */ + struct mbuf m0; + u_int af = AF_INET6; + + m0.m_next = m; + m0.m_len = 4; + m0.m_data = (char *)⁡ + +#ifdef HAVE_OLD_BPF + bpf_mtap(ifp, &m0); +#else + bpf_mtap(ifp->if_bpf, &m0); +#endif + } +#endif /*NBPFILTER > 0*/ + + /* + * Put the packet to the network layer input queue according to the + * specified address family. + * See net/if_gif.c for possible issues with packet processing + * reorder due to extra queueing. + */ + ifq = &ip6intrq; + isr = NETISR_IPV6; + + s = splimp(); + if (IF_QFULL(ifq)) { + IF_DROP(ifq); /* update statistics */ + m_freem(m); + splx(s); + return; + } + IF_ENQUEUE(ifq, m); + schednetisr(isr); + ifp->if_ipackets++; + ifp->if_ibytes += m->m_pkthdr.len; + splx(s); +} + +/* ARGSUSED */ +static void +stf_rtrequest(cmd, rt, sa) + int cmd; + struct rtentry *rt; +#if defined(__bsdi__) && _BSDI_VERSION >= 199802 + struct rt_addrinfo *sa; +#else + struct sockaddr *sa; +#endif +{ + + if (rt) + rt->rt_rmx.rmx_mtu = IPV6_MMTU; +} + +static int +stf_ioctl(ifp, cmd, data) + struct ifnet *ifp; + u_long cmd; + caddr_t data; +{ + struct ifaddr *ifa; + struct ifreq *ifr; + struct sockaddr_in6 *sin6; + int error; + + error = 0; + switch (cmd) { + case SIOCSIFADDR: + ifa = (struct ifaddr *)data; + if (ifa == NULL || ifa->ifa_addr->sa_family != AF_INET6) { + error = EAFNOSUPPORT; + break; + } + sin6 = (struct sockaddr_in6 *)ifa->ifa_addr; + if (IN6_IS_ADDR_6TO4(&sin6->sin6_addr)) { + ifa->ifa_rtrequest = stf_rtrequest; + ifp->if_flags |= IFF_UP; + } else + error = EINVAL; + break; + + case SIOCADDMULTI: + case SIOCDELMULTI: + ifr = (struct ifreq *)data; + if (ifr && ifr->ifr_addr.sa_family == AF_INET6) + ; + else + error = EAFNOSUPPORT; + break; + + default: + error = EINVAL; + break; + } + + return error; +} + +#endif /* NSTF > 0 */ Property changes on: head/sys/net/if_stf.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: head/sys/net/if_stf.h =================================================================== --- head/sys/net/if_stf.h (nonexistent) +++ head/sys/net/if_stf.h (revision 62587) @@ -0,0 +1,38 @@ +/* $FreeBSD$ */ +/* $KAME: if_stf.h,v 1.3 2000/03/25 07:23:33 sumikawa Exp $ */ + +/* + * Copyright (C) 2000 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _NET_IF_STF_H_ +#define _NET_IF_STF_H_ + +void in_stf_input __P((struct mbuf *, ...)); + +#endif /* _NET_IF_STF_H_ */ Property changes on: head/sys/net/if_stf.h ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: head/sys/net/if_types.h =================================================================== --- head/sys/net/if_types.h (revision 62586) +++ head/sys/net/if_types.h (revision 62587) @@ -1,103 +1,104 @@ /* * Copyright (c) 1989, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)if_types.h 8.2 (Berkeley) 4/20/94 * $FreeBSD$ */ #ifndef _NET_IF_TYPES_H_ #define _NET_IF_TYPES_H_ /* * Interface types for benefit of parsing media address headers. * This list is derived from the SNMP list of ifTypes, currently * documented in RFC1573. */ #define IFT_OTHER 0x1 /* none of the following */ #define IFT_1822 0x2 /* old-style arpanet imp */ #define IFT_HDH1822 0x3 /* HDH arpanet imp */ #define IFT_X25DDN 0x4 /* x25 to imp */ #define IFT_X25 0x5 /* PDN X25 interface (RFC877) */ #define IFT_ETHER 0x6 /* Ethernet CSMACD */ #define IFT_ISO88023 0x7 /* CMSA CD */ #define IFT_ISO88024 0x8 /* Token Bus */ #define IFT_ISO88025 0x9 /* Token Ring */ #define IFT_ISO88026 0xa /* MAN */ #define IFT_STARLAN 0xb #define IFT_P10 0xc /* Proteon 10MBit ring */ #define IFT_P80 0xd /* Proteon 80MBit ring */ #define IFT_HY 0xe /* Hyperchannel */ #define IFT_FDDI 0xf #define IFT_LAPB 0x10 #define IFT_SDLC 0x11 #define IFT_T1 0x12 #define IFT_CEPT 0x13 /* E1 - european T1 */ #define IFT_ISDNBASIC 0x14 #define IFT_ISDNPRIMARY 0x15 #define IFT_PTPSERIAL 0x16 /* Proprietary PTP serial */ #define IFT_PPP 0x17 /* RFC 1331 */ #define IFT_LOOP 0x18 /* loopback */ #define IFT_EON 0x19 /* ISO over IP */ #define IFT_XETHER 0x1a /* obsolete 3MB experimental ethernet */ #define IFT_NSIP 0x1b /* XNS over IP */ #define IFT_SLIP 0x1c /* IP over generic TTY */ #define IFT_ULTRA 0x1d /* Ultra Technologies */ #define IFT_DS3 0x1e /* Generic T3 */ #define IFT_SIP 0x1f /* SMDS */ #define IFT_FRELAY 0x20 /* Frame Relay DTE only */ #define IFT_RS232 0x21 #define IFT_PARA 0x22 /* parallel-port */ #define IFT_ARCNET 0x23 #define IFT_ARCNETPLUS 0x24 #define IFT_ATM 0x25 /* ATM cells */ #define IFT_MIOX25 0x26 #define IFT_SONET 0x27 /* SONET or SDH */ #define IFT_X25PLE 0x28 #define IFT_ISO88022LLC 0x29 #define IFT_LOCALTALK 0x2a #define IFT_SMDSDXI 0x2b #define IFT_FRELAYDCE 0x2c /* Frame Relay DCE */ #define IFT_V35 0x2d #define IFT_HSSI 0x2e #define IFT_HIPPI 0x2f #define IFT_MODEM 0x30 /* Generic Modem */ #define IFT_AAL5 0x31 /* AAL5 over ATM */ #define IFT_SONETPATH 0x32 #define IFT_SONETVT 0x33 #define IFT_SMDSICIP 0x34 /* SMDS InterCarrier Interface */ #define IFT_PROPVIRTUAL 0x35 /* Proprietary Virtual/internal */ #define IFT_PROPMUX 0x36 /* Proprietary Multiplexing */ #define IFT_GIF 0x37 #define IFT_FAITH 0x38 +#define IFT_STF 0x39 #endif Index: head/sys/net/net_osdep.c =================================================================== --- head/sys/net/net_osdep.c (revision 62586) +++ head/sys/net/net_osdep.c (revision 62587) @@ -1,57 +1,59 @@ +/* $FreeBSD$ */ +/* $KAME: net_osdep.c,v 1.4 2000/03/25 07:23:34 sumikawa Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include + #include const char * if_name(ifp) struct ifnet *ifp; { static char nam[IFNAMSIZ + 10]; /*enough?*/ snprintf(nam, sizeof(nam), "%s%d", ifp->if_name, ifp->if_unit); return nam; } Index: head/sys/net/net_osdep.h =================================================================== --- head/sys/net/net_osdep.h (revision 62586) +++ head/sys/net/net_osdep.h (revision 62587) @@ -1,121 +1,165 @@ +/* $FreeBSD$ */ +/* $KAME: net_osdep.h,v 1.21 2000/07/02 23:34:38 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* * glue for kernel code programming differences. */ /* * OS dependencies: * + * - struct rt_addrinfo + * all *BSDs except bsdi4 only have two members; rti_addrs and rti_info[]. + * bsdi4 has additional members; rti_flags, rti_ifa, rti_ifp, and rti_rtm. + * + * - side effects of rtrequest[1](RTM_DELETE) + * BSDI[34]: delete all cloned routes underneath the route. + * FreeBSD[234]: delete all protocol-cloned routes underneath the route. + * note that cloned routes from an interface direct route + * still remain. + * NetBSD, OpenBSD: no side effects. * - privileged process * NetBSD, FreeBSD 3 * struct proc *p; * if (p && !suser(p->p_ucred, &p->p_acflag)) * privileged; + * FreeBSD 4 + * struct proc *p; + * if (p && !suser(p)) + * privileged; * OpenBSD, BSDI [34], FreeBSD 2 * struct socket *so; * if (so->so_state & SS_PRIV) * privileged; * - foo_control * NetBSD, FreeBSD 3 * needs to give struct proc * as argument * OpenBSD, BSDI [34], FreeBSD 2 * do not need struct proc * * - bpf: * OpenBSD, NetBSD, BSDI [34] * need caddr_t * (= if_bpf **) and struct ifnet * * FreeBSD 2, FreeBSD 3 * need only struct ifnet * as argument * - struct ifnet * use queue.h? member names if name * --- --- --- * FreeBSD 2 no old standard if_name+unit * FreeBSD 3 yes strange if_name+unit * OpenBSD yes standard if_xname * NetBSD yes standard if_xname * BSDI [34] no old standard if_name+unit * - usrreq * NetBSD, OpenBSD, BSDI [34], FreeBSD 2 * single function with PRU_xx, arguments are mbuf * FreeBSD 3 * separates functions, non-mbuf arguments * - {set,get}sockopt * NetBSD, OpenBSD, BSDI [34], FreeBSD 2 * manipulation based on mbuf * FreeBSD 3 * non-mbuf manipulation using sooptcopy{in,out}() * - timeout() and untimeout() * NetBSD, OpenBSD, BSDI [34], FreeBSD 2 * timeout() is a void function * FreeBSD 3 - * timeout() is non-void, must keep returned value for untimeuot() + * timeout() is non-void, must keep returned value for untimeout() * - sysctl * NetBSD, OpenBSD * foo_sysctl() * BSDI [34] * foo_sysctl() but with different style * FreeBSD 2, FreeBSD 3 * linker hack * * - if_ioctl * NetBSD, FreeBSD 3, BSDI [34] * 2nd argument is u_long cmd * FreeBSD 2 * 2nd argument is int cmd * - if attach routines * NetBSD * void xxattach(int); * FreeBSD 2, FreeBSD 3 * void xxattach(void *); * PSEUDO_SET(xxattach, if_xx); * * - ovbcopy() * in NetBSD 1.4 or later, ovbcopy() is not supplied in the kernel. * bcopy() is safe against overwrites. * - splnet() * NetBSD 1.4 or later requires splsoftnet(). * other operating systems use splnet(). * * - dtom() * NEVER USE IT! + * + * - struct ifnet for loopback interface + * BSDI3: struct ifnet loif; + * BSDI4: struct ifnet *loifp; + * NetBSD, OpenBSD, FreeBSD2: struct ifnet loif[NLOOP]; + * + * odd thing is that many of them refers loif as ifnet *loif, + * not loif[NLOOP], from outside of if_loop.c. + * + * - number of bpf pseudo devices + * others: bpfilter.h, NBPFILTER + * FreeBSD4: bpf.h, NBPF + * solution: + * #if defined(__FreeBSD__) && __FreeBSD__ >= 4 + * #include "bpf.h" + * #define NBPFILTER NBPF + * #else + * #include "bpfilter.h" + * #endif + * + * - protosw for IPv4 (sys/netinet) + * FreeBSD4: struct ipprotosw in netinet/ipprotosw.h + * others: struct protosw in sys/protosw.h + * + * - header files with defopt (opt_xx.h) + * FreeBSD3: opt_{inet,ipsec,ip6fw,altq}.h + * FreeBSD4: opt_{inet,inet6,ipsec,ip6fw,altq}.h + * NetBSD: opt_{inet,ipsec,altq}.h + * others: does not use defopt */ #ifndef __NET_NET_OSDEP_H_DEFINED_ -#define __NET_NET_OSDEP_H_DEFINED_ +#define __NET_NET_OSDEP_H_DEFINED_ #ifdef _KERNEL struct ifnet; -extern const char *if_name __P((struct ifnet *)); +extern const char *if_name __P((struct ifnet *)); -#define HAVE_OLD_BPF +#define HAVE_OLD_BPF #endif /*_KERNEL*/ #endif /*__NET_NET_OSDEP_H_DEFINED_ */ Index: head/sys/net/pfkeyv2.h =================================================================== --- head/sys/net/pfkeyv2.h (revision 62586) +++ head/sys/net/pfkeyv2.h (revision 62587) @@ -1,420 +1,382 @@ +/* $FreeBSD$ */ +/* $KAME: pfkeyv2.h,v 1.17 2000/06/22 08:38:33 sakane Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ -/* $Id: keyv2.h,v 1.1.6.1.6.4 1999/06/08 05:33:39 itojun Exp $ */ - /* * This file has been derived rfc 2367, * And added some flags of SADB_KEY_FLAGS_ as SADB_X_EXT_. * sakane@ydc.co.jp */ #ifndef _NET_PFKEYV2_H_ -#define _NET_PFKEYV2_H_ +#define _NET_PFKEYV2_H_ /* This file defines structures and symbols for the PF_KEY Version 2 key management interface. It was written at the U.S. Naval Research Laboratory. This file is in the public domain. The authors ask that you leave this credit intact on any copies of this file. */ #ifndef __PFKEY_V2_H -#define __PFKEY_V2_H 1 +#define __PFKEY_V2_H 1 -#define PF_KEY_V2 2 -#define PFKEYV2_REVISION 199806L +#define PF_KEY_V2 2 +#define PFKEYV2_REVISION 199806L -#define SADB_RESERVED 0 -#define SADB_GETSPI 1 -#define SADB_UPDATE 2 -#define SADB_ADD 3 -#define SADB_DELETE 4 -#define SADB_GET 5 -#define SADB_ACQUIRE 6 -#define SADB_REGISTER 7 -#define SADB_EXPIRE 8 -#define SADB_FLUSH 9 -#define SADB_DUMP 10 -#define SADB_X_PROMISC 11 -#define SADB_X_PCHANGE 12 +#define SADB_RESERVED 0 +#define SADB_GETSPI 1 +#define SADB_UPDATE 2 +#define SADB_ADD 3 +#define SADB_DELETE 4 +#define SADB_GET 5 +#define SADB_ACQUIRE 6 +#define SADB_REGISTER 7 +#define SADB_EXPIRE 8 +#define SADB_FLUSH 9 +#define SADB_DUMP 10 +#define SADB_X_PROMISC 11 +#define SADB_X_PCHANGE 12 -#define SADB_X_SPDUPDATE 13 /* not yet */ -#define SADB_X_SPDADD 14 -#define SADB_X_SPDDELETE 15 -#define SADB_X_SPDGET 16 /* not yet */ -#define SADB_X_SPDACQUIRE 17 /* not yet */ -#define SADB_X_SPDDUMP 18 -#define SADB_X_SPDFLUSH 19 -#define SADB_MAX 19 +#define SADB_X_SPDUPDATE 13 +#define SADB_X_SPDADD 14 +#define SADB_X_SPDDELETE 15 /* by policy index */ +#define SADB_X_SPDGET 16 +#define SADB_X_SPDACQUIRE 17 +#define SADB_X_SPDDUMP 18 +#define SADB_X_SPDFLUSH 19 +#define SADB_X_SPDSETIDX 20 +#define SADB_X_SPDEXPIRE 21 /* not yet */ +#define SADB_X_SPDDELETE2 22 /* by policy id */ +#define SADB_MAX 22 struct sadb_msg { - u_int8_t sadb_msg_version; - u_int8_t sadb_msg_type; - u_int8_t sadb_msg_errno; - u_int8_t sadb_msg_satype; - u_int16_t sadb_msg_len; - u_int8_t sadb_msg_mode; /* XXX */ - u_int8_t sadb_msg_reserved; - u_int32_t sadb_msg_seq; - u_int32_t sadb_msg_pid; + u_int8_t sadb_msg_version; + u_int8_t sadb_msg_type; + u_int8_t sadb_msg_errno; + u_int8_t sadb_msg_satype; + u_int16_t sadb_msg_len; + u_int16_t sadb_msg_reserved; + u_int32_t sadb_msg_seq; + u_int32_t sadb_msg_pid; }; struct sadb_ext { - u_int16_t sadb_ext_len; - u_int16_t sadb_ext_type; + u_int16_t sadb_ext_len; + u_int16_t sadb_ext_type; }; struct sadb_sa { - u_int16_t sadb_sa_len; - u_int16_t sadb_sa_exttype; - u_int32_t sadb_sa_spi; - u_int8_t sadb_sa_replay; - u_int8_t sadb_sa_state; - u_int8_t sadb_sa_auth; - u_int8_t sadb_sa_encrypt; - u_int32_t sadb_sa_flags; + u_int16_t sadb_sa_len; + u_int16_t sadb_sa_exttype; + u_int32_t sadb_sa_spi; + u_int8_t sadb_sa_replay; + u_int8_t sadb_sa_state; + u_int8_t sadb_sa_auth; + u_int8_t sadb_sa_encrypt; + u_int32_t sadb_sa_flags; }; struct sadb_lifetime { - u_int16_t sadb_lifetime_len; - u_int16_t sadb_lifetime_exttype; - u_int32_t sadb_lifetime_allocations; - u_int64_t sadb_lifetime_bytes; - u_int64_t sadb_lifetime_addtime; - u_int64_t sadb_lifetime_usetime; + u_int16_t sadb_lifetime_len; + u_int16_t sadb_lifetime_exttype; + u_int32_t sadb_lifetime_allocations; + u_int64_t sadb_lifetime_bytes; + u_int64_t sadb_lifetime_addtime; + u_int64_t sadb_lifetime_usetime; }; struct sadb_address { - u_int16_t sadb_address_len; - u_int16_t sadb_address_exttype; - u_int8_t sadb_address_proto; - u_int8_t sadb_address_prefixlen; - u_int16_t sadb_address_reserved; + u_int16_t sadb_address_len; + u_int16_t sadb_address_exttype; + u_int8_t sadb_address_proto; + u_int8_t sadb_address_prefixlen; + u_int16_t sadb_address_reserved; }; struct sadb_key { - u_int16_t sadb_key_len; - u_int16_t sadb_key_exttype; - u_int16_t sadb_key_bits; - u_int16_t sadb_key_reserved; + u_int16_t sadb_key_len; + u_int16_t sadb_key_exttype; + u_int16_t sadb_key_bits; + u_int16_t sadb_key_reserved; }; struct sadb_ident { - u_int16_t sadb_ident_len; - u_int16_t sadb_ident_exttype; - u_int16_t sadb_ident_type; - u_int16_t sadb_ident_reserved; - u_int64_t sadb_ident_id; + u_int16_t sadb_ident_len; + u_int16_t sadb_ident_exttype; + u_int16_t sadb_ident_type; + u_int16_t sadb_ident_reserved; + u_int64_t sadb_ident_id; }; -/* in order to use to divide sadb_ident.sadb_ident_id */ -union sadb_x_ident_id { - u_int64_t sadb_x_ident_id; - struct _sadb_x_ident_id_addr { - u_int16_t prefix; - u_int16_t ul_proto; - u_int32_t reserved; - } sadb_x_ident_id_addr; -}; struct sadb_sens { - u_int16_t sadb_sens_len; - u_int16_t sadb_sens_exttype; - u_int32_t sadb_sens_dpd; - u_int8_t sadb_sens_sens_level; - u_int8_t sadb_sens_sens_len; - u_int8_t sadb_sens_integ_level; - u_int8_t sadb_sens_integ_len; - u_int32_t sadb_sens_reserved; + u_int16_t sadb_sens_len; + u_int16_t sadb_sens_exttype; + u_int32_t sadb_sens_dpd; + u_int8_t sadb_sens_sens_level; + u_int8_t sadb_sens_sens_len; + u_int8_t sadb_sens_integ_level; + u_int8_t sadb_sens_integ_len; + u_int32_t sadb_sens_reserved; }; struct sadb_prop { - u_int16_t sadb_prop_len; - u_int16_t sadb_prop_exttype; - u_int8_t sadb_prop_replay; - u_int8_t sadb_prop_reserved[3]; + u_int16_t sadb_prop_len; + u_int16_t sadb_prop_exttype; + u_int8_t sadb_prop_replay; + u_int8_t sadb_prop_reserved[3]; }; struct sadb_comb { - u_int8_t sadb_comb_auth; - u_int8_t sadb_comb_encrypt; - u_int16_t sadb_comb_flags; - u_int16_t sadb_comb_auth_minbits; - u_int16_t sadb_comb_auth_maxbits; - u_int16_t sadb_comb_encrypt_minbits; - u_int16_t sadb_comb_encrypt_maxbits; - u_int32_t sadb_comb_reserved; - u_int32_t sadb_comb_soft_allocations; - u_int32_t sadb_comb_hard_allocations; - u_int64_t sadb_comb_soft_bytes; - u_int64_t sadb_comb_hard_bytes; - u_int64_t sadb_comb_soft_addtime; - u_int64_t sadb_comb_hard_addtime; - u_int64_t sadb_comb_soft_usetime; - u_int64_t sadb_comb_hard_usetime; + u_int8_t sadb_comb_auth; + u_int8_t sadb_comb_encrypt; + u_int16_t sadb_comb_flags; + u_int16_t sadb_comb_auth_minbits; + u_int16_t sadb_comb_auth_maxbits; + u_int16_t sadb_comb_encrypt_minbits; + u_int16_t sadb_comb_encrypt_maxbits; + u_int32_t sadb_comb_reserved; + u_int32_t sadb_comb_soft_allocations; + u_int32_t sadb_comb_hard_allocations; + u_int64_t sadb_comb_soft_bytes; + u_int64_t sadb_comb_hard_bytes; + u_int64_t sadb_comb_soft_addtime; + u_int64_t sadb_comb_hard_addtime; + u_int64_t sadb_comb_soft_usetime; + u_int64_t sadb_comb_hard_usetime; }; struct sadb_supported { - u_int16_t sadb_supported_len; - u_int16_t sadb_supported_exttype; - u_int32_t sadb_supported_reserved; + u_int16_t sadb_supported_len; + u_int16_t sadb_supported_exttype; + u_int32_t sadb_supported_reserved; }; struct sadb_alg { - u_int8_t sadb_alg_id; - u_int8_t sadb_alg_ivlen; - u_int16_t sadb_alg_minbits; - u_int16_t sadb_alg_maxbits; - u_int16_t sadb_alg_reserved; + u_int8_t sadb_alg_id; + u_int8_t sadb_alg_ivlen; + u_int16_t sadb_alg_minbits; + u_int16_t sadb_alg_maxbits; + u_int16_t sadb_alg_reserved; }; struct sadb_spirange { - u_int16_t sadb_spirange_len; - u_int16_t sadb_spirange_exttype; - u_int32_t sadb_spirange_min; - u_int32_t sadb_spirange_max; - u_int32_t sadb_spirange_reserved; + u_int16_t sadb_spirange_len; + u_int16_t sadb_spirange_exttype; + u_int32_t sadb_spirange_min; + u_int32_t sadb_spirange_max; + u_int32_t sadb_spirange_reserved; }; struct sadb_x_kmprivate { - u_int16_t sadb_x_kmprivate_len; - u_int16_t sadb_x_kmprivate_exttype; - u_int32_t sadb_x_kmprivate_reserved; + u_int16_t sadb_x_kmprivate_len; + u_int16_t sadb_x_kmprivate_exttype; + u_int32_t sadb_x_kmprivate_reserved; }; +/* + * XXX Additional SA Extension. + * mode: tunnel or transport + * reqid: to make SA unique nevertheless the address pair of SA are same. + * Mainly it's for VPN. + */ +struct sadb_x_sa2 { + u_int16_t sadb_x_sa2_len; + u_int16_t sadb_x_sa2_exttype; + u_int8_t sadb_x_sa2_mode; + u_int8_t sadb_x_sa2_reserved1; + u_int16_t sadb_x_sa2_reserved2; + u_int32_t sadb_x_sa2_reserved3; + u_int32_t sadb_x_sa2_reqid; +}; + /* XXX Policy Extension */ -/* sizeof(struct sadb_x_policy) == 8 */ +/* sizeof(struct sadb_x_policy) == 16 */ struct sadb_x_policy { - u_int16_t sadb_x_policy_len; - u_int16_t sadb_x_policy_exttype; - /* See policy type of ipsec.h */ - u_int16_t sadb_x_policy_type; - u_int8_t sadb_x_policy_dir; /* direction, see ipsec.h */ - u_int8_t sadb_x_policy_reserved; + u_int16_t sadb_x_policy_len; + u_int16_t sadb_x_policy_exttype; + u_int16_t sadb_x_policy_type; /* See policy type of ipsec.h */ + u_int8_t sadb_x_policy_dir; /* direction, see ipsec.h */ + u_int8_t sadb_x_policy_reserved; + u_int32_t sadb_x_policy_id; + u_int32_t sadb_x_policy_reserved2; }; /* * When policy_type == IPSEC, it is followed by some of * the ipsec policy request. * [total length of ipsec policy requests] * = (sadb_x_policy_len * sizeof(uint64_t) - sizeof(struct sadb_x_policy)) */ /* XXX IPsec Policy Request Extension */ /* * This structure is aligned 8 bytes. */ struct sadb_x_ipsecrequest { - u_int16_t sadb_x_ipsecrequest_len; - /* structure length aligned to 8 bytes. - * This value is true length of bytes. - * Not in units of 64 bits. */ - u_int16_t sadb_x_ipsecrequest_proto; /* See ipsec.h */ - /* See ipsec.h. Not SADB_SATYPE_XX */ - u_int16_t sadb_x_ipsecrequest_mode; - u_int16_t sadb_x_ipsecrequest_level; /* See ipsec.h */ + u_int16_t sadb_x_ipsecrequest_len; /* structure length aligned to 8 bytes. + * This value is true length of bytes. + * Not in units of 64 bits. */ + u_int16_t sadb_x_ipsecrequest_proto; /* See ipsec.h */ + u_int8_t sadb_x_ipsecrequest_mode; /* See IPSEC_MODE_XX in ipsec.h. */ + u_int8_t sadb_x_ipsecrequest_level; /* See IPSEC_LEVEL_XX in ipsec.h */ + u_int16_t sadb_x_ipsecrequest_reqid; /* See ipsec.h */ - /* - * followed by source IP address of SA, and immediately followed by - * destination IP address of SA. These encoded into two of sockaddr - * structure without any padding. Must set each sa_len exactly. - * Each of length of the sockaddr structure are not aligned to 64bits, - * but sum of x_request and addresses is aligned to 64bits. - */ + /* + * followed by source IP address of SA, and immediately followed by + * destination IP address of SA. These encoded into two of sockaddr + * structure without any padding. Must set each sa_len exactly. + * Each of length of the sockaddr structure are not aligned to 64bits, + * but sum of x_request and addresses is aligned to 64bits. + */ }; -#define SADB_EXT_RESERVED 0 -#define SADB_EXT_SA 1 -#define SADB_EXT_LIFETIME_CURRENT 2 -#define SADB_EXT_LIFETIME_HARD 3 -#define SADB_EXT_LIFETIME_SOFT 4 -#define SADB_EXT_ADDRESS_SRC 5 -#define SADB_EXT_ADDRESS_DST 6 -#define SADB_EXT_ADDRESS_PROXY 7 -#define SADB_EXT_KEY_AUTH 8 -#define SADB_EXT_KEY_ENCRYPT 9 -#define SADB_EXT_IDENTITY_SRC 10 -#define SADB_EXT_IDENTITY_DST 11 -#define SADB_EXT_SENSITIVITY 12 -#define SADB_EXT_PROPOSAL 13 -#define SADB_EXT_SUPPORTED_AUTH 14 -#define SADB_EXT_SUPPORTED_ENCRYPT 15 -#define SADB_EXT_SPIRANGE 16 -#define SADB_X_EXT_KMPRIVATE 17 -#define SADB_X_EXT_POLICY 18 -#define SADB_EXT_MAX 18 +#define SADB_EXT_RESERVED 0 +#define SADB_EXT_SA 1 +#define SADB_EXT_LIFETIME_CURRENT 2 +#define SADB_EXT_LIFETIME_HARD 3 +#define SADB_EXT_LIFETIME_SOFT 4 +#define SADB_EXT_ADDRESS_SRC 5 +#define SADB_EXT_ADDRESS_DST 6 +#define SADB_EXT_ADDRESS_PROXY 7 +#define SADB_EXT_KEY_AUTH 8 +#define SADB_EXT_KEY_ENCRYPT 9 +#define SADB_EXT_IDENTITY_SRC 10 +#define SADB_EXT_IDENTITY_DST 11 +#define SADB_EXT_SENSITIVITY 12 +#define SADB_EXT_PROPOSAL 13 +#define SADB_EXT_SUPPORTED_AUTH 14 +#define SADB_EXT_SUPPORTED_ENCRYPT 15 +#define SADB_EXT_SPIRANGE 16 +#define SADB_X_EXT_KMPRIVATE 17 +#define SADB_X_EXT_POLICY 18 +#define SADB_X_EXT_SA2 19 +#define SADB_EXT_MAX 19 -#define SADB_SATYPE_UNSPEC 0 -#define SADB_SATYPE_AH 2 -#define SADB_SATYPE_ESP 3 -#define SADB_SATYPE_RSVP 5 -#define SADB_SATYPE_OSPFV2 6 -#define SADB_SATYPE_RIPV2 7 -#define SADB_SATYPE_MIP 8 -#define SADB_X_SATYPE_IPCOMP 9 -#define SADB_SATYPE_MAX 9 +#define SADB_SATYPE_UNSPEC 0 +#define SADB_SATYPE_AH 2 +#define SADB_SATYPE_ESP 3 +#define SADB_SATYPE_RSVP 5 +#define SADB_SATYPE_OSPFV2 6 +#define SADB_SATYPE_RIPV2 7 +#define SADB_SATYPE_MIP 8 +#define SADB_X_SATYPE_IPCOMP 9 +#define SADB_X_SATYPE_POLICY 10 +#define SADB_SATYPE_MAX 11 -#define SADB_SASTATE_LARVAL 0 -#define SADB_SASTATE_MATURE 1 -#define SADB_SASTATE_DYING 2 -#define SADB_SASTATE_DEAD 3 -#define SADB_SASTATE_MAX 3 -#define SADB_SAFLAGS_PFS 1 +#define SADB_SASTATE_LARVAL 0 +#define SADB_SASTATE_MATURE 1 +#define SADB_SASTATE_DYING 2 +#define SADB_SASTATE_DEAD 3 +#define SADB_SASTATE_MAX 3 -#define SADB_AALG_NONE 0 -#define SADB_AALG_MD5HMAC 1 /* 2 */ -#define SADB_AALG_SHA1HMAC 2 /* 3 */ -#define SADB_AALG_MD5 3 /* Keyed MD5 */ -#define SADB_AALG_SHA 4 /* Keyed SHA */ -#define SADB_AALG_NULL 5 /* null authentication */ -#define SADB_AALG_MAX 6 +#define SADB_SAFLAGS_PFS 1 -#define SADB_EALG_NONE 0 -#define SADB_EALG_DESCBC 1 /* 2 */ -#define SADB_EALG_3DESCBC 2 /* 3 */ -#define SADB_EALG_NULL 3 /* 11 */ -#define SADB_EALG_BLOWFISHCBC 4 -#define SADB_EALG_CAST128CBC 5 -#define SADB_EALG_RC5CBC 6 -#define SADB_EALG_MAX 7 +#define SADB_AALG_NONE 0 +#define SADB_AALG_MD5HMAC 1 /* 2 */ +#define SADB_AALG_SHA1HMAC 2 /* 3 */ +#define SADB_AALG_MD5 3 /* Keyed MD5 */ +#define SADB_AALG_SHA 4 /* Keyed SHA */ +#define SADB_AALG_NULL 5 /* null authentication */ +#define SADB_AALG_MAX 6 -/*nonstandard */ -#define SADB_X_CALG_NONE 0 -#define SADB_X_CALG_OUI 1 -#define SADB_X_CALG_DEFLATE 2 -#define SADB_X_CALG_LZS 3 +#define SADB_EALG_NONE 0 +#define SADB_EALG_DESCBC 1 /* 2 */ +#define SADB_EALG_3DESCBC 2 /* 3 */ +#define SADB_EALG_NULL 3 /* 11 */ +#define SADB_EALG_BLOWFISHCBC 4 +#define SADB_EALG_CAST128CBC 5 +#define SADB_EALG_RC5CBC 6 +#define SADB_EALG_MAX 7 -#define SADB_IDENTTYPE_RESERVED 0 -#define SADB_IDENTTYPE_PREFIX 1 -#define SADB_IDENTTYPE_FQDN 2 -#define SADB_IDENTTYPE_USERFQDN 3 -#define SADB_X_IDENTTYPE_ADDR 4 -#define SADB_IDENTTYPE_MAX 4 +#if 1 /*nonstandard */ +#define SADB_X_CALG_NONE 0 +#define SADB_X_CALG_OUI 1 +#define SADB_X_CALG_DEFLATE 2 +#define SADB_X_CALG_LZS 3 +#define SADB_X_CALG_MAX 4 +#endif +#define SADB_IDENTTYPE_RESERVED 0 +#define SADB_IDENTTYPE_PREFIX 1 +#define SADB_IDENTTYPE_FQDN 2 +#define SADB_IDENTTYPE_USERFQDN 3 +#define SADB_X_IDENTTYPE_ADDR 4 +#define SADB_IDENTTYPE_MAX 4 + /* `flags' in sadb_sa structure holds followings */ -#define SADB_X_EXT_NONE 0x0000 /* i.e. new format. */ -#define SADB_X_EXT_OLD 0x0001 /* old format. */ +#define SADB_X_EXT_NONE 0x0000 /* i.e. new format. */ +#define SADB_X_EXT_OLD 0x0001 /* old format. */ -#define SADB_X_EXT_IV4B 0x0010 /* IV length of 4 bytes in use */ -#define SADB_X_EXT_DERIV 0x0020 /* DES derived */ -#define SADB_X_EXT_CYCSEQ 0x0040 /* allowing to cyclic sequence. */ +#define SADB_X_EXT_IV4B 0x0010 /* IV length of 4 bytes in use */ +#define SADB_X_EXT_DERIV 0x0020 /* DES derived */ +#define SADB_X_EXT_CYCSEQ 0x0040 /* allowing to cyclic sequence. */ /* three of followings are exclusive flags each them */ -#define SADB_X_EXT_PSEQ 0x0000 /* sequencial padding for ESP */ -#define SADB_X_EXT_PRAND 0x0100 /* random padding for ESP */ -#define SADB_X_EXT_PZERO 0x0200 /* zero padding for ESP */ -#define SADB_X_EXT_PMASK 0x0300 /* mask for padding flag */ +#define SADB_X_EXT_PSEQ 0x0000 /* sequencial padding for ESP */ +#define SADB_X_EXT_PRAND 0x0100 /* random padding for ESP */ +#define SADB_X_EXT_PZERO 0x0200 /* zero padding for ESP */ +#define SADB_X_EXT_PMASK 0x0300 /* mask for padding flag */ -#define SADB_X_EXT_RAWCPI 0x0080 /* use well known CPI (IPComp) */ +#if 1 +#define SADB_X_EXT_RAWCPI 0x0080 /* use well known CPI (IPComp) */ +#endif -#define SADB_KEY_FLAGS_MAX 0x0fff +#define SADB_KEY_FLAGS_MAX 0x0fff /* SPI size for PF_KEYv2 */ -#define PFKEY_SPI_SIZE sizeof(u_int32_t) +#define PFKEY_SPI_SIZE sizeof(u_int32_t) /* Identifier for menber of lifetime structure */ -#define SADB_X_LIFETIME_ALLOCATIONS 0 -#define SADB_X_LIFETIME_BYTES 1 -#define SADB_X_LIFETIME_ADDTIME 2 -#define SADB_X_LIFETIME_USETIME 3 +#define SADB_X_LIFETIME_ALLOCATIONS 0 +#define SADB_X_LIFETIME_BYTES 1 +#define SADB_X_LIFETIME_ADDTIME 2 +#define SADB_X_LIFETIME_USETIME 3 /* The rate for SOFT lifetime against HARD one. */ -#define PFKEY_SOFT_LIFETIME_RATE 80 +#define PFKEY_SOFT_LIFETIME_RATE 80 /* Utilities */ -#define PFKEY_ALIGN8(a) (1 + (((a) - 1) | (8 - 1))) +#define PFKEY_ALIGN8(a) (1 + (((a) - 1) | (8 - 1))) #define PFKEY_EXTLEN(msg) \ PFKEY_UNUNIT64(((struct sadb_ext *)(msg))->sadb_ext_len) -#define PFKEY_ADDR_PREFIX(ext) \ +#define PFKEY_ADDR_PREFIX(ext) \ (((struct sadb_address *)(ext))->sadb_address_prefixlen) -#define PFKEY_ADDR_PROTO(ext) \ +#define PFKEY_ADDR_PROTO(ext) \ (((struct sadb_address *)(ext))->sadb_address_proto) -#define PFKEY_ADDR_SADDR(ext) \ +#define PFKEY_ADDR_SADDR(ext) \ ((struct sockaddr *)((caddr_t)(ext) + sizeof(struct sadb_address))) /* in 64bits */ #define PFKEY_UNUNIT64(a) ((a) << 3) #define PFKEY_UNIT64(a) ((a) >> 3) -#ifndef _KERNEL -struct sockaddr; +#endif /* __PFKEY_V2_H */ -int ipsec_check_keylen __P((u_int supported, u_int alg_id, u_int keylen)); -int pfkey_align __P((struct sadb_msg *msg, caddr_t *mhp)); -int pfkey_check __P((caddr_t *mhp)); -void pfkey_close __P((int so)); -u_int pfkey_get_softrate __P((u_int type)); -u_int pfkey_set_softrate __P((u_int type, u_int rate)); -int pfkey_open __P((void)); -struct sadb_msg *pfkey_recv __P((int so)); -int pfkey_recv_register __P((int so)); -int pfkey_send_register __P((int so, u_int satype)); -void pfkey_sadump __P((struct sadb_msg *m)); -void pfkey_spdump __P((struct sadb_msg *m)); -int pfkey_send __P((int so, struct sadb_msg *msg, int len)); -int pfkey_send_add __P((int so, u_int satype, u_int mode, - struct sockaddr *src, struct sockaddr *dst, - u_int32_t spi, u_int wsize, caddr_t keymat, - u_int e_type, u_int e_keylen, u_int a_type, - u_int a_keylen, u_int flags, u_int32_t l_alloc, - u_int64_t l_bytes, u_int64_t l_addtime, - u_int64_t l_usetime, u_int32_t seq)); -int pfkey_send_delete __P((int so, u_int satype, u_int mode, - struct sockaddr *src, struct sockaddr *dst, - u_int32_t spi)); -int pfkey_send_dump __P((int so, u_int satype)); -int pfkey_send_flush __P((int so, u_int satype)); -int pfkey_send_get __P((int so, u_int satype, u_int mode, - struct sockaddr *src, struct sockaddr *dst, - u_int32_t spi)); -int pfkey_send_getspi __P((int so, u_int satype, u_int mode, - struct sockaddr *src, struct sockaddr *dst, - u_int32_t min, u_int32_t max, u_int32_t seq)); -int pfkey_send_promisc_toggle __P((int so, int flag)); -int pfkey_send_spdadd __P((int so, struct sockaddr *src, u_int prefs, - struct sockaddr *dst, u_int prefd, u_int proto, - caddr_t policy, int policylen, u_int32_t seq)); -int pfkey_send_spddelete __P((int so, struct sockaddr *src, u_int prefs, - struct sockaddr *dst, u_int prefd, - u_int proto, u_int32_t seq)); -int pfkey_send_spddump __P((int so)); -int pfkey_send_spdflush __P((int so)); -int pfkey_send_update __P((int so, u_int satype, u_int mode, - struct sockaddr *src, struct sockaddr *dst, - u_int32_t spi, u_int wsize, caddr_t keymat, - u_int e_type, u_int e_keylen, u_int a_type, - u_int a_keylen, u_int flags, u_int32_t l_alloc, - u_int64_t l_bytes, u_int64_t l_addtime, - u_int64_t l_usetime, u_int32_t seq)); - -#endif /*!_KERNEL*/ - -#endif /* !__PFKEY_V2_H */ - -#endif /* !_NET_PFKEYV2_H_ */ +#endif /* _NET_PFKEYV2_H_ */ Index: head/sys/netinet/icmp6.h =================================================================== --- head/sys/netinet/icmp6.h (revision 62586) +++ head/sys/netinet/icmp6.h (revision 62587) @@ -1,39 +1,661 @@ +/* $FreeBSD$ */ +/* $KAME: icmp6.h,v 1.18 2000/07/03 02:51:08 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. + */ + +/* + * Copyright (c) 1982, 1986, 1993 + * The Regents of the University of California. All rights reserved. * - * $FreeBSD$ + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ip_icmp.h 8.1 (Berkeley) 6/10/93 */ #ifndef _NETINET_ICMP6_H_ -#define _NETINET_ICMP6_H_ +#define _NETINET_ICMP6_H_ -#define __KAME_NETINET_ICMP6_H_INCLUDED_ -#include -#undef __KAME_NETINET_ICMP6_H_INCLUDED_ +#define ICMPV6_PLD_MAXLEN 1232 /* IPV6_MMTU - sizeof(struct ip6_hdr) + - sizeof(struct icmp6_hdr) */ -#endif /* !_NETINET_ICMP6_H_ */ +struct icmp6_hdr { + u_int8_t icmp6_type; /* type field */ + u_int8_t icmp6_code; /* code field */ + u_int16_t icmp6_cksum; /* checksum field */ + union { + u_int32_t icmp6_un_data32[1]; /* type-specific field */ + u_int16_t icmp6_un_data16[2]; /* type-specific field */ + u_int8_t icmp6_un_data8[4]; /* type-specific field */ + } icmp6_dataun; +}; + +#define icmp6_data32 icmp6_dataun.icmp6_un_data32 +#define icmp6_data16 icmp6_dataun.icmp6_un_data16 +#define icmp6_data8 icmp6_dataun.icmp6_un_data8 +#define icmp6_pptr icmp6_data32[0] /* parameter prob */ +#define icmp6_mtu icmp6_data32[0] /* packet too big */ +#define icmp6_id icmp6_data16[0] /* echo request/reply */ +#define icmp6_seq icmp6_data16[1] /* echo request/reply */ +#define icmp6_maxdelay icmp6_data16[0] /* mcast group membership */ + +#define ICMP6_DST_UNREACH 1 /* dest unreachable, codes: */ +#define ICMP6_PACKET_TOO_BIG 2 /* packet too big */ +#define ICMP6_TIME_EXCEEDED 3 /* time exceeded, code: */ +#define ICMP6_PARAM_PROB 4 /* ip6 header bad */ + +#define ICMP6_ECHO_REQUEST 128 /* echo service */ +#define ICMP6_ECHO_REPLY 129 /* echo reply */ +#define ICMP6_MEMBERSHIP_QUERY 130 /* group membership query */ +#define MLD6_LISTENER_QUERY 130 /* multicast listener query */ +#define ICMP6_MEMBERSHIP_REPORT 131 /* group membership report */ +#define MLD6_LISTENER_REPORT 131 /* multicast listener report */ +#define ICMP6_MEMBERSHIP_REDUCTION 132 /* group membership termination */ +#define MLD6_LISTENER_DONE 132 /* multicast listener done */ + +#define ND_ROUTER_SOLICIT 133 /* router solicitation */ +#define ND_ROUTER_ADVERT 134 /* router advertisment */ +#define ND_NEIGHBOR_SOLICIT 135 /* neighbor solicitation */ +#define ND_NEIGHBOR_ADVERT 136 /* neighbor advertisment */ +#define ND_REDIRECT 137 /* redirect */ + +#define ICMP6_ROUTER_RENUMBERING 138 /* router renumbering */ + +#define ICMP6_WRUREQUEST 139 /* who are you request */ +#define ICMP6_WRUREPLY 140 /* who are you reply */ +#define ICMP6_FQDN_QUERY 139 /* FQDN query */ +#define ICMP6_FQDN_REPLY 140 /* FQDN reply */ +#define ICMP6_NI_QUERY 139 /* node information request */ +#define ICMP6_NI_REPLY 140 /* node information reply */ + +/* The definitions below are experimental. TBA */ +#define MLD6_MTRACE_RESP 141 /* mtrace response(to sender) */ +#define MLD6_MTRACE 142 /* mtrace messages */ + +#define ICMP6_MAXTYPE 142 + +#define ICMP6_DST_UNREACH_NOROUTE 0 /* no route to destination */ +#define ICMP6_DST_UNREACH_ADMIN 1 /* administratively prohibited */ +#define ICMP6_DST_UNREACH_NOTNEIGHBOR 2 /* not a neighbor(obsolete) */ +#define ICMP6_DST_UNREACH_BEYONDSCOPE 2 /* beyond scope of source address */ +#define ICMP6_DST_UNREACH_ADDR 3 /* address unreachable */ +#define ICMP6_DST_UNREACH_NOPORT 4 /* port unreachable */ + +#define ICMP6_TIME_EXCEED_TRANSIT 0 /* ttl==0 in transit */ +#define ICMP6_TIME_EXCEED_REASSEMBLY 1 /* ttl==0 in reass */ + +#define ICMP6_PARAMPROB_HEADER 0 /* erroneous header field */ +#define ICMP6_PARAMPROB_NEXTHEADER 1 /* unrecognized next header */ +#define ICMP6_PARAMPROB_OPTION 2 /* unrecognized option */ + +#define ICMP6_INFOMSG_MASK 0x80 /* all informational messages */ + +#define ICMP6_NI_SUBJ_IPV6 0 /* Query Subject is an IPv6 address */ +#define ICMP6_NI_SUBJ_FQDN 1 /* Query Subject is a Domain name */ +#define ICMP6_NI_SUBJ_IPV4 2 /* Query Subject is an IPv4 address */ + +#define ICMP6_NI_SUCESS 0 /* node information successful reply */ +#define ICMP6_NI_REFUSED 1 /* node information request is refused */ +#define ICMP6_NI_UNKNOWN 2 /* unknown Qtype */ + +#define ICMP6_ROUTER_RENUMBERING_COMMAND 0 /* rr command */ +#define ICMP6_ROUTER_RENUMBERING_RESULT 1 /* rr result */ +#define ICMP6_ROUTER_RENUMBERING_SEQNUM_RESET 255 /* rr seq num reset */ + +/* Used in kernel only */ +#define ND_REDIRECT_ONLINK 0 /* redirect to an on-link node */ +#define ND_REDIRECT_ROUTER 1 /* redirect to a better router */ + +/* + * Multicast Listener Discovery + */ +struct mld6_hdr { + struct icmp6_hdr mld6_hdr; + struct in6_addr mld6_addr; /* multicast address */ +}; + +#define mld6_type mld6_hdr.icmp6_type +#define mld6_code mld6_hdr.icmp6_code +#define mld6_cksum mld6_hdr.icmp6_cksum +#define mld6_maxdelay mld6_hdr.icmp6_data16[0] +#define mld6_reserved mld6_hdr.icmp6_data16[1] + +/* + * Neighbor Discovery + */ + +struct nd_router_solicit { /* router solicitation */ + struct icmp6_hdr nd_rs_hdr; + /* could be followed by options */ +}; + +#define nd_rs_type nd_rs_hdr.icmp6_type +#define nd_rs_code nd_rs_hdr.icmp6_code +#define nd_rs_cksum nd_rs_hdr.icmp6_cksum +#define nd_rs_reserved nd_rs_hdr.icmp6_data32[0] + +struct nd_router_advert { /* router advertisement */ + struct icmp6_hdr nd_ra_hdr; + u_int32_t nd_ra_reachable; /* reachable time */ + u_int32_t nd_ra_retransmit; /* retransmit timer */ + /* could be followed by options */ +}; + +#define nd_ra_type nd_ra_hdr.icmp6_type +#define nd_ra_code nd_ra_hdr.icmp6_code +#define nd_ra_cksum nd_ra_hdr.icmp6_cksum +#define nd_ra_curhoplimit nd_ra_hdr.icmp6_data8[0] +#define nd_ra_flags_reserved nd_ra_hdr.icmp6_data8[1] +#define ND_RA_FLAG_MANAGED 0x80 +#define ND_RA_FLAG_OTHER 0x40 +#define nd_ra_router_lifetime nd_ra_hdr.icmp6_data16[1] + +struct nd_neighbor_solicit { /* neighbor solicitation */ + struct icmp6_hdr nd_ns_hdr; + struct in6_addr nd_ns_target; /*target address */ + /* could be followed by options */ +}; + +#define nd_ns_type nd_ns_hdr.icmp6_type +#define nd_ns_code nd_ns_hdr.icmp6_code +#define nd_ns_cksum nd_ns_hdr.icmp6_cksum +#define nd_ns_reserved nd_ns_hdr.icmp6_data32[0] + +struct nd_neighbor_advert { /* neighbor advertisement */ + struct icmp6_hdr nd_na_hdr; + struct in6_addr nd_na_target; /* target address */ + /* could be followed by options */ +}; + +#define nd_na_type nd_na_hdr.icmp6_type +#define nd_na_code nd_na_hdr.icmp6_code +#define nd_na_cksum nd_na_hdr.icmp6_cksum +#define nd_na_flags_reserved nd_na_hdr.icmp6_data32[0] +#if BYTE_ORDER == BIG_ENDIAN +#define ND_NA_FLAG_ROUTER 0x80000000 +#define ND_NA_FLAG_SOLICITED 0x40000000 +#define ND_NA_FLAG_OVERRIDE 0x20000000 +#else +#if BYTE_ORDER == LITTLE_ENDIAN +#define ND_NA_FLAG_ROUTER 0x80 +#define ND_NA_FLAG_SOLICITED 0x40 +#define ND_NA_FLAG_OVERRIDE 0x20 +#endif +#endif + +struct nd_redirect { /* redirect */ + struct icmp6_hdr nd_rd_hdr; + struct in6_addr nd_rd_target; /* target address */ + struct in6_addr nd_rd_dst; /* destination address */ + /* could be followed by options */ +}; + +#define nd_rd_type nd_rd_hdr.icmp6_type +#define nd_rd_code nd_rd_hdr.icmp6_code +#define nd_rd_cksum nd_rd_hdr.icmp6_cksum +#define nd_rd_reserved nd_rd_hdr.icmp6_data32[0] + +struct nd_opt_hdr { /* Neighbor discovery option header */ + u_int8_t nd_opt_type; + u_int8_t nd_opt_len; + /* followed by option specific data*/ +}; + +#define ND_OPT_SOURCE_LINKADDR 1 +#define ND_OPT_TARGET_LINKADDR 2 +#define ND_OPT_PREFIX_INFORMATION 3 +#define ND_OPT_REDIRECTED_HEADER 4 +#define ND_OPT_MTU 5 + +struct nd_opt_prefix_info { /* prefix information */ + u_int8_t nd_opt_pi_type; + u_int8_t nd_opt_pi_len; + u_int8_t nd_opt_pi_prefix_len; + u_int8_t nd_opt_pi_flags_reserved; + u_int32_t nd_opt_pi_valid_time; + u_int32_t nd_opt_pi_preferred_time; + u_int32_t nd_opt_pi_reserved2; + struct in6_addr nd_opt_pi_prefix; +}; + +#define ND_OPT_PI_FLAG_ONLINK 0x80 +#define ND_OPT_PI_FLAG_AUTO 0x40 + +struct nd_opt_rd_hdr { /* redirected header */ + u_int8_t nd_opt_rh_type; + u_int8_t nd_opt_rh_len; + u_int16_t nd_opt_rh_reserved1; + u_int32_t nd_opt_rh_reserved2; + /* followed by IP header and data */ +}; + +struct nd_opt_mtu { /* MTU option */ + u_int8_t nd_opt_mtu_type; + u_int8_t nd_opt_mtu_len; + u_int16_t nd_opt_mtu_reserved; + u_int32_t nd_opt_mtu_mtu; +}; + +/* + * icmp6 namelookup + */ + +struct icmp6_namelookup { + struct icmp6_hdr icmp6_nl_hdr; + u_int8_t icmp6_nl_nonce[8]; + int32_t icmp6_nl_ttl; +#if 0 + u_int8_t icmp6_nl_len; + u_int8_t icmp6_nl_name[3]; +#endif + /* could be followed by options */ +}; + +/* + * icmp6 node information + */ +struct icmp6_nodeinfo { + struct icmp6_hdr icmp6_ni_hdr; + u_int8_t icmp6_ni_nonce[8]; + /* could be followed by reply data */ +}; + +#define ni_type icmp6_ni_hdr.icmp6_type +#define ni_code icmp6_ni_hdr.icmp6_code +#define ni_cksum icmp6_ni_hdr.icmp6_cksum +#define ni_qtype icmp6_ni_hdr.icmp6_data16[0] +#define ni_flags icmp6_ni_hdr.icmp6_data16[1] + +#define NI_QTYPE_NOOP 0 /* NOOP */ +#define NI_QTYPE_SUPTYPES 1 /* Supported Qtypes */ +#define NI_QTYPE_FQDN 2 /* FQDN */ +#define NI_QTYPE_NODEADDR 3 /* Node Addresses. XXX: spec says 2, but it may be a typo... */ + +#if BYTE_ORDER == BIG_ENDIAN +#define NI_SUPTYPE_FLAG_COMPRESS 0x1 +#define NI_FQDN_FLAG_VALIDTTL 0x1 +#elif BYTE_ORDER == LITTLE_ENDIAN +#define NI_SUPTYPE_FLAG_COMPRESS 0x0100 +#define NI_FQDN_FLAG_VALIDTTL 0x0100 +#endif + +#ifdef NAME_LOOKUPS_04 +#if BYTE_ORDER == BIG_ENDIAN +#define NI_NODEADDR_FLAG_LINKLOCAL 0x1 +#define NI_NODEADDR_FLAG_SITELOCAL 0x2 +#define NI_NODEADDR_FLAG_GLOBAL 0x4 +#define NI_NODEADDR_FLAG_ALL 0x8 +#define NI_NODEADDR_FLAG_TRUNCATE 0x10 +#define NI_NODEADDR_FLAG_ANYCAST 0x20 /* just experimental. not in spec */ +#elif BYTE_ORDER == LITTLE_ENDIAN +#define NI_NODEADDR_FLAG_LINKLOCAL 0x0100 +#define NI_NODEADDR_FLAG_SITELOCAL 0x0200 +#define NI_NODEADDR_FLAG_GLOBAL 0x0400 +#define NI_NODEADDR_FLAG_ALL 0x0800 +#define NI_NODEADDR_FLAG_TRUNCATE 0x1000 +#define NI_NODEADDR_FLAG_ANYCAST 0x2000 /* just experimental. not in spec */ +#endif +#else /* draft-ietf-ipngwg-icmp-name-lookups-05 (and later?) */ +#if BYTE_ORDER == BIG_ENDIAN +#define NI_NODEADDR_FLAG_TRUNCATE 0x1 +#define NI_NODEADDR_FLAG_ALL 0x2 +#define NI_NODEADDR_FLAG_COMPAT 0x4 +#define NI_NODEADDR_FLAG_LINKLOCAL 0x8 +#define NI_NODEADDR_FLAG_SITELOCAL 0x10 +#define NI_NODEADDR_FLAG_GLOBAL 0x20 +#define NI_NODEADDR_FLAG_ANYCAST 0x40 /* just experimental. not in spec */ +#elif BYTE_ORDER == LITTLE_ENDIAN +#define NI_NODEADDR_FLAG_TRUNCATE 0x0100 +#define NI_NODEADDR_FLAG_ALL 0x0200 +#define NI_NODEADDR_FLAG_COMPAT 0x0400 +#define NI_NODEADDR_FLAG_LINKLOCAL 0x0800 +#define NI_NODEADDR_FLAG_SITELOCAL 0x1000 +#define NI_NODEADDR_FLAG_GLOBAL 0x2000 +#define NI_NODEADDR_FLAG_ANYCAST 0x4000 /* just experimental. not in spec */ +#endif +#endif + +struct ni_reply_fqdn { + u_int32_t ni_fqdn_ttl; /* TTL */ + u_int8_t ni_fqdn_namelen; /* length in octets of the FQDN */ + u_int8_t ni_fqdn_name[3]; /* XXX: alignment */ +}; + +/* + * Router Renumbering. as router-renum-08.txt + */ +struct icmp6_router_renum { /* router renumbering header */ + struct icmp6_hdr rr_hdr; + u_int8_t rr_segnum; + u_int8_t rr_flags; + u_int16_t rr_maxdelay; + u_int32_t rr_reserved; +}; +#define ICMP6_RR_FLAGS_SEGNUM 0x80 +#define ICMP6_RR_FLAGS_TEST 0x40 +#define ICMP6_RR_FLAGS_REQRESULT 0x20 +#define ICMP6_RR_FLAGS_FORCEAPPLY 0x10 +#define ICMP6_RR_FLAGS_SPECSITE 0x08 +#define ICMP6_RR_FLAGS_PREVDONE 0x04 + +#define rr_type rr_hdr.icmp6_type +#define rr_code rr_hdr.icmp6_code +#define rr_cksum rr_hdr.icmp6_cksum +#define rr_seqnum rr_hdr.icmp6_data32[0] + +struct rr_pco_match { /* match prefix part */ + u_int8_t rpm_code; + u_int8_t rpm_len; + u_int8_t rpm_ordinal; + u_int8_t rpm_matchlen; + u_int8_t rpm_minlen; + u_int8_t rpm_maxlen; + u_int16_t rpm_reserved; + struct in6_addr rpm_prefix; +}; + +#define RPM_PCO_ADD 1 +#define RPM_PCO_CHANGE 2 +#define RPM_PCO_SETGLOBAL 3 +#define RPM_PCO_MAX 4 + +struct rr_pco_use { /* use prefix part */ + u_int8_t rpu_uselen; + u_int8_t rpu_keeplen; + u_int8_t rpu_ramask; + u_int8_t rpu_raflags; + u_int32_t rpu_vltime; + u_int32_t rpu_pltime; + u_int32_t rpu_flags; + struct in6_addr rpu_prefix; +}; +#define ICMP6_RR_PCOUSE_RAFLAGS_ONLINK 0x80 +#define ICMP6_RR_PCOUSE_RAFLAGS_AUTO 0x40 + +#if BYTE_ORDER == BIG_ENDIAN +#define ICMP6_RR_PCOUSE_FLAGS_DECRVLTIME 0x80000000 +#define ICMP6_RR_PCOUSE_FLAGS_DECRPLTIME 0x40000000 +#elif BYTE_ORDER == LITTLE_ENDIAN +#define ICMP6_RR_PCOUSE_FLAGS_DECRVLTIME 0x80 +#define ICMP6_RR_PCOUSE_FLAGS_DECRPLTIME 0x40 +#endif + +struct rr_result { /* router renumbering result message */ + u_int16_t rrr_flags; + u_int8_t rrr_ordinal; + u_int8_t rrr_matchedlen; + u_int32_t rrr_ifid; + struct in6_addr rrr_prefix; +}; +#if BYTE_ORDER == BIG_ENDIAN +#define ICMP6_RR_RESULT_FLAGS_OOB 0x0002 +#define ICMP6_RR_RESULT_FLAGS_FORBIDDEN 0x0001 +#elif BYTE_ORDER == LITTLE_ENDIAN +#define ICMP6_RR_RESULT_FLAGS_OOB 0x02 +#define ICMP6_RR_RESULT_FLAGS_FORBIDDEN 0x01 +#endif + +/* + * icmp6 filter structures. + */ + +struct icmp6_filter { + u_int32_t icmp6_filt[8]; +}; + +#ifdef _KERNEL +#define ICMP6_FILTER_SETPASSALL(filterp) \ +do { \ + int i; u_char *p; \ + p = (u_char *)filterp; \ + for (i = 0; i < sizeof(struct icmp6_filter); i++) \ + p[i] = 0xff; \ +} while (0) +#define ICMP6_FILTER_SETBLOCKALL(filterp) \ + bzero(filterp, sizeof(struct icmp6_filter)) +#else /* _KERNEL */ +#define ICMP6_FILTER_SETPASSALL(filterp) \ + memset(filterp, 0xff, sizeof(struct icmp6_filter)) +#define ICMP6_FILTER_SETBLOCKALL(filterp) \ + memset(filterp, 0x00, sizeof(struct icmp6_filter)) +#endif /* _KERNEL */ + +#define ICMP6_FILTER_SETPASS(type, filterp) \ + (((filterp)->icmp6_filt[(type) >> 5]) |= (1 << ((type) & 31))) +#define ICMP6_FILTER_SETBLOCK(type, filterp) \ + (((filterp)->icmp6_filt[(type) >> 5]) &= ~(1 << ((type) & 31))) +#define ICMP6_FILTER_WILLPASS(type, filterp) \ + ((((filterp)->icmp6_filt[(type) >> 5]) & (1 << ((type) & 31))) != 0) +#define ICMP6_FILTER_WILLBLOCK(type, filterp) \ + ((((filterp)->icmp6_filt[(type) >> 5]) & (1 << ((type) & 31))) == 0) + +/* + * Variables related to this implementation + * of the internet control message protocol version 6. + */ +struct icmp6errstat { + u_quad_t icp6errs_dst_unreach_noroute; + u_quad_t icp6errs_dst_unreach_admin; + u_quad_t icp6errs_dst_unreach_beyondscope; + u_quad_t icp6errs_dst_unreach_addr; + u_quad_t icp6errs_dst_unreach_noport; + u_quad_t icp6errs_packet_too_big; + u_quad_t icp6errs_time_exceed_transit; + u_quad_t icp6errs_time_exceed_reassembly; + u_quad_t icp6errs_paramprob_header; + u_quad_t icp6errs_paramprob_nextheader; + u_quad_t icp6errs_paramprob_option; + u_quad_t icp6errs_redirect; /* we regard redirect as an error here */ + u_quad_t icp6errs_unknown; +}; + +struct icmp6stat { +/* statistics related to icmp6 packets generated */ + u_quad_t icp6s_error; /* # of calls to icmp6_error */ + u_quad_t icp6s_canterror; /* no error 'cuz old was icmp */ + u_quad_t icp6s_toofreq; /* no error 'cuz rate limitation */ + u_quad_t icp6s_outhist[256]; +/* statistics related to input message processed */ + u_quad_t icp6s_badcode; /* icmp6_code out of range */ + u_quad_t icp6s_tooshort; /* packet < sizeof(struct icmp6_hdr) */ + u_quad_t icp6s_checksum; /* bad checksum */ + u_quad_t icp6s_badlen; /* calculated bound mismatch */ + u_quad_t icp6s_reflect; /* number of responses */ + u_quad_t icp6s_inhist[256]; + u_quad_t icp6s_nd_toomanyopt; /* too many ND options */ + struct icmp6errstat icp6s_outerrhist; +#define icp6s_odst_unreach_noroute \ + icp6s_outerrhist.icp6errs_dst_unreach_noroute +#define icp6s_odst_unreach_admin icp6s_outerrhist.icp6errs_dst_unreach_admin +#define icp6s_odst_unreach_beyondscope \ + icp6s_outerrhist.icp6errs_dst_unreach_beyondscope +#define icp6s_odst_unreach_addr icp6s_outerrhist.icp6errs_dst_unreach_addr +#define icp6s_odst_unreach_noport icp6s_outerrhist.icp6errs_dst_unreach_noport +#define icp6s_opacket_too_big icp6s_outerrhist.icp6errs_packet_too_big +#define icp6s_otime_exceed_transit \ + icp6s_outerrhist.icp6errs_time_exceed_transit +#define icp6s_otime_exceed_reassembly \ + icp6s_outerrhist.icp6errs_time_exceed_reassembly +#define icp6s_oparamprob_header icp6s_outerrhist.icp6errs_paramprob_header +#define icp6s_oparamprob_nextheader \ + icp6s_outerrhist.icp6errs_paramprob_nextheader +#define icp6s_oparamprob_option icp6s_outerrhist.icp6errs_paramprob_option +#define icp6s_oredirect icp6s_outerrhist.icp6errs_redirect +#define icp6s_ounknown icp6s_outerrhist.icp6errs_unknown +}; + +/* + * Names for ICMP sysctl objects + */ +#define ICMPV6CTL_STATS 1 +#define ICMPV6CTL_REDIRACCEPT 2 /* accept/process redirects */ +#define ICMPV6CTL_REDIRTIMEOUT 3 /* redirect cache time */ +#define ICMPV6CTL_ERRRATELIMIT 5 /* ICMPv6 error rate limitation */ +#define ICMPV6CTL_ND6_PRUNE 6 +#define ICMPV6CTL_ND6_DELAY 8 +#define ICMPV6CTL_ND6_UMAXTRIES 9 +#define ICMPV6CTL_ND6_MMAXTRIES 10 +#define ICMPV6CTL_ND6_USELOOPBACK 11 +/*#define ICMPV6CTL_ND6_PROXYALL 12 obsoleted, do not reuse here */ +#define ICMPV6CTL_NODEINFO 13 +#define ICMPV6CTL_ERRPPSLIMIT 14 /* ICMPv6 error pps limitation */ +#define ICMPV6CTL_ND6_MAXNUDHINT 15 +#define ICMPV6CTL_MAXID 16 + +#define ICMPV6CTL_NAMES { \ + { 0, 0 }, \ + { 0, 0 }, \ + { "rediraccept", CTLTYPE_INT }, \ + { "redirtimeout", CTLTYPE_INT }, \ + { 0, 0 }, \ + { "errratelimit", CTLTYPE_INT }, \ + { "nd6_prune", CTLTYPE_INT }, \ + { 0, 0 }, \ + { "nd6_delay", CTLTYPE_INT }, \ + { "nd6_umaxtries", CTLTYPE_INT }, \ + { "nd6_mmaxtries", CTLTYPE_INT }, \ + { "nd6_useloopback", CTLTYPE_INT }, \ + { 0, 0 }, \ + { "nodeinfo", CTLTYPE_INT }, \ + { "errppslimit", CTLTYPE_INT }, \ + { "nd6_maxnudhint", CTLTYPE_INT }, \ +} + +#define RTF_PROBEMTU RTF_PROTO1 + +#ifdef _KERNEL +# ifdef __STDC__ +struct rtentry; +struct rttimer; +struct in6_multi; +# endif +void icmp6_init __P((void)); +void icmp6_paramerror __P((struct mbuf *, int)); +void icmp6_error __P((struct mbuf *, int, int, int)); +int icmp6_input __P((struct mbuf **, int *, int)); +void icmp6_fasttimo __P((void)); +void icmp6_reflect __P((struct mbuf *, size_t)); +void icmp6_prepare __P((struct mbuf *)); +void icmp6_redirect_input __P((struct mbuf *, int)); +void icmp6_redirect_output __P((struct mbuf *, struct rtentry *)); + +/* XXX: is this the right place for these macros? */ +#define icmp6_ifstat_inc(ifp, tag) \ +do { \ + if ((ifp) && (ifp)->if_index <= if_index \ + && (ifp)->if_index < icmp6_ifstatmax \ + && icmp6_ifstat && icmp6_ifstat[(ifp)->if_index]) { \ + icmp6_ifstat[(ifp)->if_index]->tag++; \ + } \ +} while (0) + +#define icmp6_ifoutstat_inc(ifp, type, code) \ +do { \ + icmp6_ifstat_inc(ifp, ifs6_out_msg); \ + if (type < ICMP6_INFOMSG_MASK) \ + icmp6_ifstat_inc(ifp, ifs6_out_error); \ + switch(type) { \ + case ICMP6_DST_UNREACH: \ + icmp6_ifstat_inc(ifp, ifs6_out_dstunreach); \ + if (code == ICMP6_DST_UNREACH_ADMIN) \ + icmp6_ifstat_inc(ifp, ifs6_out_adminprohib); \ + break; \ + case ICMP6_PACKET_TOO_BIG: \ + icmp6_ifstat_inc(ifp, ifs6_out_pkttoobig); \ + break; \ + case ICMP6_TIME_EXCEEDED: \ + icmp6_ifstat_inc(ifp, ifs6_out_timeexceed); \ + break; \ + case ICMP6_PARAM_PROB: \ + icmp6_ifstat_inc(ifp, ifs6_out_paramprob); \ + break; \ + case ICMP6_ECHO_REQUEST: \ + icmp6_ifstat_inc(ifp, ifs6_out_echo); \ + break; \ + case ICMP6_ECHO_REPLY: \ + icmp6_ifstat_inc(ifp, ifs6_out_echoreply); \ + break; \ + case MLD6_LISTENER_QUERY: \ + icmp6_ifstat_inc(ifp, ifs6_out_mldquery); \ + break; \ + case MLD6_LISTENER_REPORT: \ + icmp6_ifstat_inc(ifp, ifs6_out_mldreport); \ + break; \ + case MLD6_LISTENER_DONE: \ + icmp6_ifstat_inc(ifp, ifs6_out_mlddone); \ + break; \ + case ND_ROUTER_SOLICIT: \ + icmp6_ifstat_inc(ifp, ifs6_out_routersolicit); \ + break; \ + case ND_ROUTER_ADVERT: \ + icmp6_ifstat_inc(ifp, ifs6_out_routeradvert); \ + break; \ + case ND_NEIGHBOR_SOLICIT: \ + icmp6_ifstat_inc(ifp, ifs6_out_neighborsolicit); \ + break; \ + case ND_NEIGHBOR_ADVERT: \ + icmp6_ifstat_inc(ifp, ifs6_out_neighboradvert); \ + break; \ + case ND_REDIRECT: \ + icmp6_ifstat_inc(ifp, ifs6_out_redirect); \ + break; \ + } \ +} while (0) + +extern int icmp6_rediraccept; /* accept/process redirects */ +extern int icmp6_redirtimeout; /* cache time for redirect routes */ +#endif /* _KERNEL */ + +#endif /* not _NETINET_ICMP6_H_ */ Index: head/sys/netinet/in.c =================================================================== --- head/sys/netinet/in.c (revision 62586) +++ head/sys/netinet/in.c (revision 62587) @@ -1,853 +1,854 @@ /* * Copyright (c) 1982, 1986, 1991, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)in.c 8.4 (Berkeley) 1/9/95 * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gif.h" #if NGIF > 0 #include #endif static MALLOC_DEFINE(M_IPMADDR, "in_multi", "internet multicast address"); static int in_mask2len __P((struct in_addr *)); static void in_len2mask __P((struct in_addr *, int)); static int in_lifaddr_ioctl __P((struct socket *, u_long, caddr_t, struct ifnet *, struct proc *)); static void in_socktrim __P((struct sockaddr_in *)); static int in_ifinit __P((struct ifnet *, struct in_ifaddr *, struct sockaddr_in *, int)); static int subnetsarelocal = 0; SYSCTL_INT(_net_inet_ip, OID_AUTO, subnets_are_local, CTLFLAG_RW, &subnetsarelocal, 0, ""); struct in_multihead in_multihead; /* XXX BSS initialization */ /* * Return 1 if an internet address is for a ``local'' host * (one to which we have a connection). If subnetsarelocal * is true, this includes other subnets of the local net. * Otherwise, it includes only the directly-connected (sub)nets. */ int in_localaddr(in) struct in_addr in; { register u_long i = ntohl(in.s_addr); register struct in_ifaddr *ia; if (subnetsarelocal) { for (ia = in_ifaddrhead.tqh_first; ia; ia = ia->ia_link.tqe_next) if ((i & ia->ia_netmask) == ia->ia_net) return (1); } else { for (ia = in_ifaddrhead.tqh_first; ia; ia = ia->ia_link.tqe_next) if ((i & ia->ia_subnetmask) == ia->ia_subnet) return (1); } return (0); } /* * Determine whether an IP address is in a reserved set of addresses * that may not be forwarded, or whether datagrams to that destination * may be forwarded. */ int in_canforward(in) struct in_addr in; { register u_long i = ntohl(in.s_addr); register u_long net; if (IN_EXPERIMENTAL(i) || IN_MULTICAST(i)) return (0); if (IN_CLASSA(i)) { net = i & IN_CLASSA_NET; if (net == 0 || net == (IN_LOOPBACKNET << IN_CLASSA_NSHIFT)) return (0); } return (1); } /* * Trim a mask in a sockaddr */ static void in_socktrim(ap) struct sockaddr_in *ap; { register char *cplim = (char *) &ap->sin_addr; register char *cp = (char *) (&ap->sin_addr + 1); ap->sin_len = 0; while (--cp >= cplim) if (*cp) { (ap)->sin_len = cp - (char *) (ap) + 1; break; } } static int in_mask2len(mask) struct in_addr *mask; { int x, y; u_char *p; p = (u_char *)mask; for (x = 0; x < sizeof(*mask); x++) { if (p[x] != 0xff) break; } y = 0; if (x < sizeof(*mask)) { for (y = 0; y < 8; y++) { if ((p[x] & (0x80 >> y)) == 0) break; } } return x * 8 + y; } static void in_len2mask(mask, len) struct in_addr *mask; int len; { int i; u_char *p; p = (u_char *)mask; bzero(mask, sizeof(*mask)); for (i = 0; i < len / 8; i++) p[i] = 0xff; if (len % 8) p[i] = (0xff00 >> (len % 8)) & 0xff; } static int in_interfaces; /* number of external internet interfaces */ /* * Generic internet control operations (ioctl's). * Ifp is 0 if not an interface-specific ioctl. */ /* ARGSUSED */ int in_control(so, cmd, data, ifp, p) struct socket *so; u_long cmd; caddr_t data; register struct ifnet *ifp; struct proc *p; { register struct ifreq *ifr = (struct ifreq *)data; register struct in_ifaddr *ia = 0, *iap; register struct ifaddr *ifa; struct in_ifaddr *oia; struct in_aliasreq *ifra = (struct in_aliasreq *)data; struct sockaddr_in oldaddr; int error, hostIsNew, maskIsNew, s; u_long i; #if NGIF > 0 if (ifp && ifp->if_type == IFT_GIF) { switch (cmd) { case SIOCSIFPHYADDR: + case SIOCDIFPHYADDR: if (p && (error = suser(p)) != 0) return(error); case SIOCGIFPSRCADDR: case SIOCGIFPDSTADDR: return gif_ioctl(ifp, cmd, data); } } #endif switch (cmd) { case SIOCALIFADDR: case SIOCDLIFADDR: if (p && (error = suser(p)) != 0) return error; /*fall through*/ case SIOCGLIFADDR: if (!ifp) return EINVAL; return in_lifaddr_ioctl(so, cmd, data, ifp, p); } /* * Find address for this interface, if it exists. * * If an alias address was specified, find that one instead of * the first one on the interface. */ if (ifp) for (iap = in_ifaddrhead.tqh_first; iap; iap = iap->ia_link.tqe_next) if (iap->ia_ifp == ifp) { if (((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr.s_addr == iap->ia_addr.sin_addr.s_addr) { ia = iap; break; } else if (ia == NULL) { ia = iap; if (ifr->ifr_addr.sa_family != AF_INET) break; } } switch (cmd) { case SIOCAIFADDR: case SIOCDIFADDR: if (ifp == 0) return (EADDRNOTAVAIL); if (ifra->ifra_addr.sin_family == AF_INET) { for (oia = ia; ia; ia = ia->ia_link.tqe_next) { if (ia->ia_ifp == ifp && ia->ia_addr.sin_addr.s_addr == ifra->ifra_addr.sin_addr.s_addr) break; } if ((ifp->if_flags & IFF_POINTOPOINT) && (cmd == SIOCAIFADDR) && (ifra->ifra_dstaddr.sin_addr.s_addr == INADDR_ANY)) { return EDESTADDRREQ; } } if (cmd == SIOCDIFADDR && ia == 0) return (EADDRNOTAVAIL); /* FALLTHROUGH */ case SIOCSIFADDR: case SIOCSIFNETMASK: case SIOCSIFDSTADDR: if (p && (error = suser(p)) != 0) return error; if (ifp == 0) return (EADDRNOTAVAIL); if (ia == (struct in_ifaddr *)0) { ia = (struct in_ifaddr *) malloc(sizeof *ia, M_IFADDR, M_WAITOK); if (ia == (struct in_ifaddr *)NULL) return (ENOBUFS); bzero((caddr_t)ia, sizeof *ia); /* * Protect from ipintr() traversing address list * while we're modifying it. */ s = splnet(); TAILQ_INSERT_TAIL(&in_ifaddrhead, ia, ia_link); ifa = &ia->ia_ifa; TAILQ_INSERT_TAIL(&ifp->if_addrhead, ifa, ifa_link); ifa->ifa_addr = (struct sockaddr *)&ia->ia_addr; ifa->ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr; ifa->ifa_netmask = (struct sockaddr *)&ia->ia_sockmask; ia->ia_sockmask.sin_len = 8; if (ifp->if_flags & IFF_BROADCAST) { ia->ia_broadaddr.sin_len = sizeof(ia->ia_addr); ia->ia_broadaddr.sin_family = AF_INET; } ia->ia_ifp = ifp; if (!(ifp->if_flags & IFF_LOOPBACK)) in_interfaces++; splx(s); } break; case SIOCSIFBRDADDR: if (p && (error = suser(p)) != 0) return error; /* FALLTHROUGH */ case SIOCGIFADDR: case SIOCGIFNETMASK: case SIOCGIFDSTADDR: case SIOCGIFBRDADDR: if (ia == (struct in_ifaddr *)0) return (EADDRNOTAVAIL); break; } switch (cmd) { case SIOCGIFADDR: *((struct sockaddr_in *)&ifr->ifr_addr) = ia->ia_addr; break; case SIOCGIFBRDADDR: if ((ifp->if_flags & IFF_BROADCAST) == 0) return (EINVAL); *((struct sockaddr_in *)&ifr->ifr_dstaddr) = ia->ia_broadaddr; break; case SIOCGIFDSTADDR: if ((ifp->if_flags & IFF_POINTOPOINT) == 0) return (EINVAL); *((struct sockaddr_in *)&ifr->ifr_dstaddr) = ia->ia_dstaddr; break; case SIOCGIFNETMASK: *((struct sockaddr_in *)&ifr->ifr_addr) = ia->ia_sockmask; break; case SIOCSIFDSTADDR: if ((ifp->if_flags & IFF_POINTOPOINT) == 0) return (EINVAL); oldaddr = ia->ia_dstaddr; ia->ia_dstaddr = *(struct sockaddr_in *)&ifr->ifr_dstaddr; if (ifp->if_ioctl && (error = (*ifp->if_ioctl) (ifp, SIOCSIFDSTADDR, (caddr_t)ia))) { ia->ia_dstaddr = oldaddr; return (error); } if (ia->ia_flags & IFA_ROUTE) { ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&oldaddr; rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST); ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr; rtinit(&(ia->ia_ifa), (int)RTM_ADD, RTF_HOST|RTF_UP); } break; case SIOCSIFBRDADDR: if ((ifp->if_flags & IFF_BROADCAST) == 0) return (EINVAL); ia->ia_broadaddr = *(struct sockaddr_in *)&ifr->ifr_broadaddr; break; case SIOCSIFADDR: return (in_ifinit(ifp, ia, (struct sockaddr_in *) &ifr->ifr_addr, 1)); case SIOCSIFNETMASK: i = ifra->ifra_addr.sin_addr.s_addr; ia->ia_subnetmask = ntohl(ia->ia_sockmask.sin_addr.s_addr = i); break; case SIOCAIFADDR: maskIsNew = 0; hostIsNew = 1; error = 0; if (ia->ia_addr.sin_family == AF_INET) { if (ifra->ifra_addr.sin_len == 0) { ifra->ifra_addr = ia->ia_addr; hostIsNew = 0; } else if (ifra->ifra_addr.sin_addr.s_addr == ia->ia_addr.sin_addr.s_addr) hostIsNew = 0; } if (ifra->ifra_mask.sin_len) { in_ifscrub(ifp, ia); ia->ia_sockmask = ifra->ifra_mask; ia->ia_subnetmask = ntohl(ia->ia_sockmask.sin_addr.s_addr); maskIsNew = 1; } if ((ifp->if_flags & IFF_POINTOPOINT) && (ifra->ifra_dstaddr.sin_family == AF_INET)) { in_ifscrub(ifp, ia); ia->ia_dstaddr = ifra->ifra_dstaddr; maskIsNew = 1; /* We lie; but the effect's the same */ } if (ifra->ifra_addr.sin_family == AF_INET && (hostIsNew || maskIsNew)) error = in_ifinit(ifp, ia, &ifra->ifra_addr, 0); if ((ifp->if_flags & IFF_BROADCAST) && (ifra->ifra_broadaddr.sin_family == AF_INET)) ia->ia_broadaddr = ifra->ifra_broadaddr; return (error); case SIOCDIFADDR: in_ifscrub(ifp, ia); /* * Protect from ipintr() traversing address list * while we're modifying it. */ s = splnet(); ifa = &ia->ia_ifa; TAILQ_REMOVE(&ifp->if_addrhead, ifa, ifa_link); oia = ia; TAILQ_REMOVE(&in_ifaddrhead, oia, ia_link); IFAFREE(&oia->ia_ifa); splx(s); break; default: if (ifp == 0 || ifp->if_ioctl == 0) return (EOPNOTSUPP); return ((*ifp->if_ioctl)(ifp, cmd, data)); } return (0); } /* * SIOC[GAD]LIFADDR. * SIOCGLIFADDR: get first address. (?!?) * SIOCGLIFADDR with IFLR_PREFIX: * get first address that matches the specified prefix. * SIOCALIFADDR: add the specified address. * SIOCALIFADDR with IFLR_PREFIX: * EINVAL since we can't deduce hostid part of the address. * SIOCDLIFADDR: delete the specified address. * SIOCDLIFADDR with IFLR_PREFIX: * delete the first address that matches the specified prefix. * return values: * EINVAL on invalid parameters * EADDRNOTAVAIL on prefix match failed/specified address not found * other values may be returned from in_ioctl() */ static int in_lifaddr_ioctl(so, cmd, data, ifp, p) struct socket *so; u_long cmd; caddr_t data; struct ifnet *ifp; struct proc *p; { struct if_laddrreq *iflr = (struct if_laddrreq *)data; struct ifaddr *ifa; /* sanity checks */ if (!data || !ifp) { panic("invalid argument to in_lifaddr_ioctl"); /*NOTRECHED*/ } switch (cmd) { case SIOCGLIFADDR: /* address must be specified on GET with IFLR_PREFIX */ if ((iflr->flags & IFLR_PREFIX) == 0) break; /*FALLTHROUGH*/ case SIOCALIFADDR: case SIOCDLIFADDR: /* address must be specified on ADD and DELETE */ if (iflr->addr.ss_family != AF_INET) return EINVAL; if (iflr->addr.ss_len != sizeof(struct sockaddr_in)) return EINVAL; /* XXX need improvement */ if (iflr->dstaddr.ss_family && iflr->dstaddr.ss_family != AF_INET) return EINVAL; if (iflr->dstaddr.ss_family && iflr->dstaddr.ss_len != sizeof(struct sockaddr_in)) return EINVAL; break; default: /*shouldn't happen*/ return EOPNOTSUPP; } if (sizeof(struct in_addr) * 8 < iflr->prefixlen) return EINVAL; switch (cmd) { case SIOCALIFADDR: { struct in_aliasreq ifra; if (iflr->flags & IFLR_PREFIX) return EINVAL; /* copy args to in_aliasreq, perform ioctl(SIOCAIFADDR_IN6). */ bzero(&ifra, sizeof(ifra)); bcopy(iflr->iflr_name, ifra.ifra_name, sizeof(ifra.ifra_name)); bcopy(&iflr->addr, &ifra.ifra_addr, iflr->addr.ss_len); if (iflr->dstaddr.ss_family) { /*XXX*/ bcopy(&iflr->dstaddr, &ifra.ifra_dstaddr, iflr->dstaddr.ss_len); } ifra.ifra_mask.sin_family = AF_INET; ifra.ifra_mask.sin_len = sizeof(struct sockaddr_in); in_len2mask(&ifra.ifra_mask.sin_addr, iflr->prefixlen); return in_control(so, SIOCAIFADDR, (caddr_t)&ifra, ifp, p); } case SIOCGLIFADDR: case SIOCDLIFADDR: { struct in_ifaddr *ia; struct in_addr mask, candidate, match; struct sockaddr_in *sin; int cmp; bzero(&mask, sizeof(mask)); if (iflr->flags & IFLR_PREFIX) { /* lookup a prefix rather than address. */ in_len2mask(&mask, iflr->prefixlen); sin = (struct sockaddr_in *)&iflr->addr; match.s_addr = sin->sin_addr.s_addr; match.s_addr &= mask.s_addr; /* if you set extra bits, that's wrong */ if (match.s_addr != sin->sin_addr.s_addr) return EINVAL; cmp = 1; } else { if (cmd == SIOCGLIFADDR) { /* on getting an address, take the 1st match */ cmp = 0; /*XXX*/ } else { /* on deleting an address, do exact match */ in_len2mask(&mask, 32); sin = (struct sockaddr_in *)&iflr->addr; match.s_addr = sin->sin_addr.s_addr; cmp = 1; } } TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; if (!cmp) break; candidate.s_addr = ((struct sockaddr_in *)&ifa->ifa_addr)->sin_addr.s_addr; candidate.s_addr &= mask.s_addr; if (candidate.s_addr == match.s_addr) break; } if (!ifa) return EADDRNOTAVAIL; ia = (struct in_ifaddr *)ifa; if (cmd == SIOCGLIFADDR) { /* fill in the if_laddrreq structure */ bcopy(&ia->ia_addr, &iflr->addr, ia->ia_addr.sin_len); if ((ifp->if_flags & IFF_POINTOPOINT) != 0) { bcopy(&ia->ia_dstaddr, &iflr->dstaddr, ia->ia_dstaddr.sin_len); } else bzero(&iflr->dstaddr, sizeof(iflr->dstaddr)); iflr->prefixlen = in_mask2len(&ia->ia_sockmask.sin_addr); iflr->flags = 0; /*XXX*/ return 0; } else { struct in_aliasreq ifra; /* fill in_aliasreq and do ioctl(SIOCDIFADDR_IN6) */ bzero(&ifra, sizeof(ifra)); bcopy(iflr->iflr_name, ifra.ifra_name, sizeof(ifra.ifra_name)); bcopy(&ia->ia_addr, &ifra.ifra_addr, ia->ia_addr.sin_len); if ((ifp->if_flags & IFF_POINTOPOINT) != 0) { bcopy(&ia->ia_dstaddr, &ifra.ifra_dstaddr, ia->ia_dstaddr.sin_len); } bcopy(&ia->ia_sockmask, &ifra.ifra_dstaddr, ia->ia_sockmask.sin_len); return in_control(so, SIOCDIFADDR, (caddr_t)&ifra, ifp, p); } } } return EOPNOTSUPP; /*just for safety*/ } /* * Delete any existing route for an interface. */ void in_ifscrub(ifp, ia) register struct ifnet *ifp; register struct in_ifaddr *ia; { if ((ia->ia_flags & IFA_ROUTE) == 0) return; if (ifp->if_flags & (IFF_LOOPBACK|IFF_POINTOPOINT)) rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST); else rtinit(&(ia->ia_ifa), (int)RTM_DELETE, 0); ia->ia_flags &= ~IFA_ROUTE; } /* * Initialize an interface's internet address * and routing table entry. */ static int in_ifinit(ifp, ia, sin, scrub) register struct ifnet *ifp; register struct in_ifaddr *ia; struct sockaddr_in *sin; int scrub; { register u_long i = ntohl(sin->sin_addr.s_addr); struct sockaddr_in oldaddr; int s = splimp(), flags = RTF_UP, error; oldaddr = ia->ia_addr; ia->ia_addr = *sin; /* * Give the interface a chance to initialize * if this is its first address, * and to validate the address if necessary. */ if (ifp->if_ioctl && (error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia))) { splx(s); ia->ia_addr = oldaddr; return (error); } splx(s); if (scrub) { ia->ia_ifa.ifa_addr = (struct sockaddr *)&oldaddr; in_ifscrub(ifp, ia); ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr; } if (IN_CLASSA(i)) ia->ia_netmask = IN_CLASSA_NET; else if (IN_CLASSB(i)) ia->ia_netmask = IN_CLASSB_NET; else ia->ia_netmask = IN_CLASSC_NET; /* * The subnet mask usually includes at least the standard network part, * but may may be smaller in the case of supernetting. * If it is set, we believe it. */ if (ia->ia_subnetmask == 0) { ia->ia_subnetmask = ia->ia_netmask; ia->ia_sockmask.sin_addr.s_addr = htonl(ia->ia_subnetmask); } else ia->ia_netmask &= ia->ia_subnetmask; ia->ia_net = i & ia->ia_netmask; ia->ia_subnet = i & ia->ia_subnetmask; in_socktrim(&ia->ia_sockmask); /* * Add route for the network. */ ia->ia_ifa.ifa_metric = ifp->if_metric; if (ifp->if_flags & IFF_BROADCAST) { ia->ia_broadaddr.sin_addr.s_addr = htonl(ia->ia_subnet | ~ia->ia_subnetmask); ia->ia_netbroadcast.s_addr = htonl(ia->ia_net | ~ ia->ia_netmask); } else if (ifp->if_flags & IFF_LOOPBACK) { ia->ia_ifa.ifa_dstaddr = ia->ia_ifa.ifa_addr; flags |= RTF_HOST; } else if (ifp->if_flags & IFF_POINTOPOINT) { if (ia->ia_dstaddr.sin_family != AF_INET) return (0); flags |= RTF_HOST; } if ((error = rtinit(&(ia->ia_ifa), (int)RTM_ADD, flags)) == 0) ia->ia_flags |= IFA_ROUTE; /* * If the interface supports multicast, join the "all hosts" * multicast group on that interface. */ if (ifp->if_flags & IFF_MULTICAST) { struct in_addr addr; addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP); in_addmulti(&addr, ifp); } return (error); } /* * Return 1 if the address might be a local broadcast address. */ int in_broadcast(in, ifp) struct in_addr in; struct ifnet *ifp; { register struct ifaddr *ifa; u_long t; if (in.s_addr == INADDR_BROADCAST || in.s_addr == INADDR_ANY) return 1; if ((ifp->if_flags & IFF_BROADCAST) == 0) return 0; t = ntohl(in.s_addr); /* * Look through the list of addresses for a match * with a broadcast address. */ #define ia ((struct in_ifaddr *)ifa) for (ifa = ifp->if_addrhead.tqh_first; ifa; ifa = ifa->ifa_link.tqe_next) if (ifa->ifa_addr->sa_family == AF_INET && (in.s_addr == ia->ia_broadaddr.sin_addr.s_addr || in.s_addr == ia->ia_netbroadcast.s_addr || /* * Check for old-style (host 0) broadcast. */ t == ia->ia_subnet || t == ia->ia_net) && /* * Check for an all one subnetmask. These * only exist when an interface gets a secondary * address. */ ia->ia_subnetmask != (u_long)0xffffffff) return 1; return (0); #undef ia } /* * Add an address to the list of IP multicast addresses for a given interface. */ struct in_multi * in_addmulti(ap, ifp) register struct in_addr *ap; register struct ifnet *ifp; { register struct in_multi *inm; int error; struct sockaddr_in sin; struct ifmultiaddr *ifma; int s = splnet(); /* * Call generic routine to add membership or increment * refcount. It wants addresses in the form of a sockaddr, * so we build one here (being careful to zero the unused bytes). */ bzero(&sin, sizeof sin); sin.sin_family = AF_INET; sin.sin_len = sizeof sin; sin.sin_addr = *ap; error = if_addmulti(ifp, (struct sockaddr *)&sin, &ifma); if (error) { splx(s); return 0; } /* * If ifma->ifma_protospec is null, then if_addmulti() created * a new record. Otherwise, we are done. */ if (ifma->ifma_protospec != 0) return ifma->ifma_protospec; /* XXX - if_addmulti uses M_WAITOK. Can this really be called at interrupt time? If so, need to fix if_addmulti. XXX */ inm = (struct in_multi *)malloc(sizeof(*inm), M_IPMADDR, M_NOWAIT); if (inm == NULL) { splx(s); return (NULL); } bzero(inm, sizeof *inm); inm->inm_addr = *ap; inm->inm_ifp = ifp; inm->inm_ifma = ifma; ifma->ifma_protospec = inm; LIST_INSERT_HEAD(&in_multihead, inm, inm_link); /* * Let IGMP know that we have joined a new IP multicast group. */ igmp_joingroup(inm); splx(s); return (inm); } /* * Delete a multicast address record. */ void in_delmulti(inm) register struct in_multi *inm; { struct ifmultiaddr *ifma = inm->inm_ifma; struct in_multi my_inm; int s = splnet(); my_inm.inm_ifp = NULL ; /* don't send the leave msg */ if (ifma->ifma_refcount == 1) { /* * No remaining claims to this record; let IGMP know that * we are leaving the multicast group. * But do it after the if_delmulti() which might reset * the interface and nuke the packet. */ my_inm = *inm ; ifma->ifma_protospec = 0; LIST_REMOVE(inm, inm_link); free(inm, M_IPMADDR); } /* XXX - should be separate API for when we have an ifma? */ if_delmulti(ifma->ifma_ifp, ifma->ifma_addr); if (my_inm.inm_ifp != NULL) igmp_leavegroup(&my_inm); splx(s); } Index: head/sys/netinet/in.h =================================================================== --- head/sys/netinet/in.h (revision 62586) +++ head/sys/netinet/in.h (revision 62587) @@ -1,463 +1,492 @@ /* * Copyright (c) 1982, 1986, 1990, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)in.h 8.3 (Berkeley) 1/3/94 * $FreeBSD$ */ #ifndef _NETINET_IN_H_ #define _NETINET_IN_H_ /* * Constants and structures defined by the internet system, * Per RFC 790, September 1981, and numerous additions. */ /* * Protocols (RFC 1700) */ #define IPPROTO_IP 0 /* dummy for IP */ #define IPPROTO_HOPOPTS 0 /* IP6 hop-by-hop options */ #define IPPROTO_ICMP 1 /* control message protocol */ #define IPPROTO_IGMP 2 /* group mgmt protocol */ #define IPPROTO_GGP 3 /* gateway^2 (deprecated) */ #define IPPROTO_IPV4 4 /* IPv4 encapsulation */ #define IPPROTO_IPIP IPPROTO_IPV4 /* for compatibility */ #define IPPROTO_TCP 6 /* tcp */ #define IPPROTO_ST 7 /* Stream protocol II */ #define IPPROTO_EGP 8 /* exterior gateway protocol */ #define IPPROTO_PIGP 9 /* private interior gateway */ #define IPPROTO_RCCMON 10 /* BBN RCC Monitoring */ #define IPPROTO_NVPII 11 /* network voice protocol*/ #define IPPROTO_PUP 12 /* pup */ #define IPPROTO_ARGUS 13 /* Argus */ #define IPPROTO_EMCON 14 /* EMCON */ #define IPPROTO_XNET 15 /* Cross Net Debugger */ #define IPPROTO_CHAOS 16 /* Chaos*/ #define IPPROTO_UDP 17 /* user datagram protocol */ #define IPPROTO_MUX 18 /* Multiplexing */ #define IPPROTO_MEAS 19 /* DCN Measurement Subsystems */ #define IPPROTO_HMP 20 /* Host Monitoring */ #define IPPROTO_PRM 21 /* Packet Radio Measurement */ #define IPPROTO_IDP 22 /* xns idp */ #define IPPROTO_TRUNK1 23 /* Trunk-1 */ #define IPPROTO_TRUNK2 24 /* Trunk-2 */ #define IPPROTO_LEAF1 25 /* Leaf-1 */ #define IPPROTO_LEAF2 26 /* Leaf-2 */ #define IPPROTO_RDP 27 /* Reliable Data */ #define IPPROTO_IRTP 28 /* Reliable Transaction */ #define IPPROTO_TP 29 /* tp-4 w/ class negotiation */ #define IPPROTO_BLT 30 /* Bulk Data Transfer */ #define IPPROTO_NSP 31 /* Network Services */ #define IPPROTO_INP 32 /* Merit Internodal */ #define IPPROTO_SEP 33 /* Sequential Exchange */ #define IPPROTO_3PC 34 /* Third Party Connect */ #define IPPROTO_IDPR 35 /* InterDomain Policy Routing */ #define IPPROTO_XTP 36 /* XTP */ #define IPPROTO_DDP 37 /* Datagram Delivery */ #define IPPROTO_CMTP 38 /* Control Message Transport */ #define IPPROTO_TPXX 39 /* TP++ Transport */ #define IPPROTO_IL 40 /* IL transport protocol */ #define IPPROTO_IPV6 41 /* IP6 header */ #define IPPROTO_SDRP 42 /* Source Demand Routing */ #define IPPROTO_ROUTING 43 /* IP6 routing header */ #define IPPROTO_FRAGMENT 44 /* IP6 fragmentation header */ #define IPPROTO_IDRP 45 /* InterDomain Routing*/ #define IPPROTO_RSVP 46 /* resource reservation */ #define IPPROTO_GRE 47 /* General Routing Encap. */ #define IPPROTO_MHRP 48 /* Mobile Host Routing */ #define IPPROTO_BHA 49 /* BHA */ #define IPPROTO_ESP 50 /* IP6 Encap Sec. Payload */ #define IPPROTO_AH 51 /* IP6 Auth Header */ #define IPPROTO_INLSP 52 /* Integ. Net Layer Security */ #define IPPROTO_SWIPE 53 /* IP with encryption */ #define IPPROTO_NHRP 54 /* Next Hop Resolution */ /* 55-57: Unassigned */ #define IPPROTO_ICMPV6 58 /* ICMP6 */ #define IPPROTO_NONE 59 /* IP6 no next header */ #define IPPROTO_DSTOPTS 60 /* IP6 destination option */ #define IPPROTO_AHIP 61 /* any host internal protocol */ #define IPPROTO_CFTP 62 /* CFTP */ #define IPPROTO_HELLO 63 /* "hello" routing protocol */ #define IPPROTO_SATEXPAK 64 /* SATNET/Backroom EXPAK */ #define IPPROTO_KRYPTOLAN 65 /* Kryptolan */ #define IPPROTO_RVD 66 /* Remote Virtual Disk */ #define IPPROTO_IPPC 67 /* Pluribus Packet Core */ #define IPPROTO_ADFS 68 /* Any distributed FS */ #define IPPROTO_SATMON 69 /* Satnet Monitoring */ #define IPPROTO_VISA 70 /* VISA Protocol */ #define IPPROTO_IPCV 71 /* Packet Core Utility */ #define IPPROTO_CPNX 72 /* Comp. Prot. Net. Executive */ #define IPPROTO_CPHB 73 /* Comp. Prot. HeartBeat */ #define IPPROTO_WSN 74 /* Wang Span Network */ #define IPPROTO_PVP 75 /* Packet Video Protocol */ #define IPPROTO_BRSATMON 76 /* BackRoom SATNET Monitoring */ #define IPPROTO_ND 77 /* Sun net disk proto (temp.) */ #define IPPROTO_WBMON 78 /* WIDEBAND Monitoring */ #define IPPROTO_WBEXPAK 79 /* WIDEBAND EXPAK */ #define IPPROTO_EON 80 /* ISO cnlp */ #define IPPROTO_VMTP 81 /* VMTP */ #define IPPROTO_SVMTP 82 /* Secure VMTP */ #define IPPROTO_VINES 83 /* Banyon VINES */ #define IPPROTO_TTP 84 /* TTP */ #define IPPROTO_IGP 85 /* NSFNET-IGP */ #define IPPROTO_DGP 86 /* dissimilar gateway prot. */ #define IPPROTO_TCF 87 /* TCF */ #define IPPROTO_IGRP 88 /* Cisco/GXS IGRP */ #define IPPROTO_OSPFIGP 89 /* OSPFIGP */ #define IPPROTO_SRPC 90 /* Strite RPC protocol */ #define IPPROTO_LARP 91 /* Locus Address Resoloution */ #define IPPROTO_MTP 92 /* Multicast Transport */ #define IPPROTO_AX25 93 /* AX.25 Frames */ #define IPPROTO_IPEIP 94 /* IP encapsulated in IP */ #define IPPROTO_MICP 95 /* Mobile Int.ing control */ #define IPPROTO_SCCSP 96 /* Semaphore Comm. security */ #define IPPROTO_ETHERIP 97 /* Ethernet IP encapsulation */ #define IPPROTO_ENCAP 98 /* encapsulation header */ #define IPPROTO_APES 99 /* any private encr. scheme */ #define IPPROTO_GMTP 100 /* GMTP*/ #define IPPROTO_IPCOMP 108 /* payload compression (IPComp) */ /* 101-254: Partly Unassigned */ #define IPPROTO_PIM 103 /* Protocol Independent Mcast */ #define IPPROTO_PGM 113 /* PGM */ /* 255: Reserved */ /* BSD Private, local use, namespace incursion */ #define IPPROTO_DIVERT 254 /* divert pseudo-protocol */ #define IPPROTO_RAW 255 /* raw IP packet */ #define IPPROTO_MAX 256 /* last return value of *_input(), meaning "all job for this pkt is done". */ #define IPPROTO_DONE 257 /* * Local port number conventions: * * When a user does a bind(2) or connect(2) with a port number of zero, * a non-conflicting local port address is chosen. * The default range is IPPORT_RESERVED through * IPPORT_USERRESERVED, although that is settable by sysctl. * * A user may set the IPPROTO_IP option IP_PORTRANGE to change this * default assignment range. * * The value IP_PORTRANGE_DEFAULT causes the default behavior. * * The value IP_PORTRANGE_HIGH changes the range of candidate port numbers * into the "high" range. These are reserved for client outbound connections * which do not want to be filtered by any firewalls. * * The value IP_PORTRANGE_LOW changes the range to the "low" are * that is (by convention) restricted to privileged processes. This * convention is based on "vouchsafe" principles only. It is only secure * if you trust the remote host to restrict these ports. * * The default range of ports and the high range can be changed by * sysctl(3). (net.inet.ip.port{hi,low}{first,last}_auto) * * Changing those values has bad security implications if you are * using a a stateless firewall that is allowing packets outside of that * range in order to allow transparent outgoing connections. * * Such a firewall configuration will generally depend on the use of these * default values. If you change them, you may find your Security * Administrator looking for you with a heavy object. * * For a slightly more orthodox text view on this: * * ftp://ftp.isi.edu/in-notes/iana/assignments/port-numbers * * port numbers are divided into three ranges: * * 0 - 1023 Well Known Ports * 1024 - 49151 Registered Ports * 49152 - 65535 Dynamic and/or Private Ports * */ /* * Ports < IPPORT_RESERVED are reserved for * privileged processes (e.g. root). (IP_PORTRANGE_LOW) * Ports > IPPORT_USERRESERVED are reserved * for servers, not necessarily privileged. (IP_PORTRANGE_DEFAULT) */ #define IPPORT_RESERVED 1024 #define IPPORT_USERRESERVED 5000 /* * Default local port range to use by setting IP_PORTRANGE_HIGH */ #define IPPORT_HIFIRSTAUTO 49152 #define IPPORT_HILASTAUTO 65535 /* * Scanning for a free reserved port return a value below IPPORT_RESERVED, * but higher than IPPORT_RESERVEDSTART. Traditionally the start value was * 512, but that conflicts with some well-known-services that firewalls may * have a fit if we use. */ #define IPPORT_RESERVEDSTART 600 /* * Internet address (a structure for historical reasons) */ struct in_addr { u_int32_t s_addr; }; /* * Definitions of bits in internet address integers. * On subnets, the decomposition of addresses to host and net parts * is done according to subnet mask, not the masks here. */ #define IN_CLASSA(i) (((u_int32_t)(i) & 0x80000000) == 0) #define IN_CLASSA_NET 0xff000000 #define IN_CLASSA_NSHIFT 24 #define IN_CLASSA_HOST 0x00ffffff #define IN_CLASSA_MAX 128 #define IN_CLASSB(i) (((u_int32_t)(i) & 0xc0000000) == 0x80000000) #define IN_CLASSB_NET 0xffff0000 #define IN_CLASSB_NSHIFT 16 #define IN_CLASSB_HOST 0x0000ffff #define IN_CLASSB_MAX 65536 #define IN_CLASSC(i) (((u_int32_t)(i) & 0xe0000000) == 0xc0000000) #define IN_CLASSC_NET 0xffffff00 #define IN_CLASSC_NSHIFT 8 #define IN_CLASSC_HOST 0x000000ff #define IN_CLASSD(i) (((u_int32_t)(i) & 0xf0000000) == 0xe0000000) #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(i) IN_CLASSD(i) #define IN_EXPERIMENTAL(i) (((u_int32_t)(i) & 0xf0000000) == 0xf0000000) #define IN_BADCLASS(i) (((u_int32_t)(i) & 0xf0000000) == 0xf0000000) #define INADDR_ANY (u_int32_t)0x00000000 #define INADDR_LOOPBACK (u_int32_t)0x7f000001 #define INADDR_BROADCAST (u_int32_t)0xffffffff /* must be masked */ #ifndef _KERNEL #define INADDR_NONE 0xffffffff /* -1 return */ #endif #define INADDR_UNSPEC_GROUP (u_int32_t)0xe0000000 /* 224.0.0.0 */ #define INADDR_ALLHOSTS_GROUP (u_int32_t)0xe0000001 /* 224.0.0.1 */ #define INADDR_ALLRTRS_GROUP (u_int32_t)0xe0000002 /* 224.0.0.2 */ #define INADDR_MAX_LOCAL_GROUP (u_int32_t)0xe00000ff /* 224.0.0.255 */ #define IN_LOOPBACKNET 127 /* official! */ /* * Socket address, internet style. */ struct sockaddr_in { u_char sin_len; u_char sin_family; u_short sin_port; struct in_addr sin_addr; char sin_zero[8]; }; #define INET_ADDRSTRLEN 16 /* * Structure used to describe IP options. * Used to store options internally, to pass them to a process, * or to restore options retrieved earlier. * The ip_dst is used for the first-hop gateway when using a source route * (this gets put into the header proper). */ struct ip_opts { struct in_addr ip_dst; /* first hop, 0 w/o src rt */ char ip_opts[40]; /* actually variable in size */ }; /* * Options for use with [gs]etsockopt at the IP level. * First word of comment is data type; bool is stored in int. */ #define IP_OPTIONS 1 /* buf/ip_opts; set/get IP options */ #define IP_HDRINCL 2 /* int; header is included with data */ #define IP_TOS 3 /* int; IP type of service and preced. */ #define IP_TTL 4 /* int; IP time to live */ #define IP_RECVOPTS 5 /* bool; receive all IP opts w/dgram */ #define IP_RECVRETOPTS 6 /* bool; receive IP opts for response */ #define IP_RECVDSTADDR 7 /* bool; receive IP dst addr w/dgram */ #define IP_RETOPTS 8 /* ip_opts; set/get IP options */ #define IP_MULTICAST_IF 9 /* u_char; set/get IP multicast i/f */ #define IP_MULTICAST_TTL 10 /* u_char; set/get IP multicast ttl */ #define IP_MULTICAST_LOOP 11 /* u_char; set/get IP multicast loopback */ #define IP_ADD_MEMBERSHIP 12 /* ip_mreq; add an IP group membership */ #define IP_DROP_MEMBERSHIP 13 /* ip_mreq; drop an IP group membership */ #define IP_MULTICAST_VIF 14 /* set/get IP mcast virt. iface */ #define IP_RSVP_ON 15 /* enable RSVP in kernel */ #define IP_RSVP_OFF 16 /* disable RSVP in kernel */ #define IP_RSVP_VIF_ON 17 /* set RSVP per-vif socket */ #define IP_RSVP_VIF_OFF 18 /* unset RSVP per-vif socket */ #define IP_PORTRANGE 19 /* int; range to choose for unspec port */ #define IP_RECVIF 20 /* bool; receive reception if w/dgram */ /* for IPSEC */ #define IP_IPSEC_POLICY 21 /* int; set/get security policy */ #define IP_FAITH 22 /* bool; accept FAITH'ed connections */ #define IP_FW_ADD 50 /* add a firewall rule to chain */ #define IP_FW_DEL 51 /* delete a firewall rule from chain */ #define IP_FW_FLUSH 52 /* flush firewall rule chain */ #define IP_FW_ZERO 53 /* clear single/all firewall counter(s) */ #define IP_FW_GET 54 /* get entire firewall rule chain */ #define IP_FW_RESETLOG 55 /* reset logging counters */ #define IP_DUMMYNET_CONFIGURE 60 /* add/configure a dummynet pipe */ #define IP_DUMMYNET_DEL 61 /* delete a dummynet pipe from chain */ #define IP_DUMMYNET_FLUSH 62 /* flush dummynet */ #define IP_DUMMYNET_GET 64 /* get entire dummynet pipes */ /* * Defaults and limits for options */ #define IP_DEFAULT_MULTICAST_TTL 1 /* normally limit m'casts to 1 hop */ #define IP_DEFAULT_MULTICAST_LOOP 1 /* normally hear sends if a member */ #define IP_MAX_MEMBERSHIPS 20 /* per socket */ /* * Argument structure for IP_ADD_MEMBERSHIP and IP_DROP_MEMBERSHIP. */ struct ip_mreq { struct in_addr imr_multiaddr; /* IP multicast address of group */ struct in_addr imr_interface; /* local IP address of interface */ }; /* * Argument for IP_PORTRANGE: * - which range to search when port is unspecified at bind() or connect() */ #define IP_PORTRANGE_DEFAULT 0 /* default range */ #define IP_PORTRANGE_HIGH 1 /* "high" - request firewall bypass */ #define IP_PORTRANGE_LOW 2 /* "low" - vouchsafe security */ /* * Definitions for inet sysctl operations. * * Third level is protocol number. * Fourth level is desired variable within that protocol. */ -#define IPPROTO_MAXID (IPPROTO_ESP + 1) /* don't list to IPPROTO_MAX */ +#define IPPROTO_MAXID (IPPROTO_AH + 1) /* don't list to IPPROTO_MAX */ #define CTL_IPPROTO_NAMES { \ { "ip", CTLTYPE_NODE }, \ { "icmp", CTLTYPE_NODE }, \ { "igmp", CTLTYPE_NODE }, \ { "ggp", CTLTYPE_NODE }, \ { 0, 0 }, \ { 0, 0 }, \ { "tcp", CTLTYPE_NODE }, \ { 0, 0 }, \ { "egp", CTLTYPE_NODE }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { "pup", CTLTYPE_NODE }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { "udp", CTLTYPE_NODE }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { "idp", CTLTYPE_NODE }, \ + { 0, 0 }, \ + { 0, 0 }, \ + { 0, 0 }, \ + { 0, 0 }, \ + { 0, 0 }, \ + { 0, 0 }, \ + { 0, 0 }, \ + { 0, 0 }, \ + { 0, 0 }, \ + { 0, 0 }, \ + { 0, 0 }, \ + { 0, 0 }, \ + { 0, 0 }, \ + { 0, 0 }, \ + { 0, 0 }, \ + { 0, 0 }, \ + { 0, 0 }, \ + { 0, 0 }, \ + { 0, 0 }, \ + { 0, 0 }, \ + { 0, 0 }, \ + { 0, 0 }, \ + { 0, 0 }, \ + { 0, 0 }, \ + { 0, 0 }, \ + { 0, 0 }, \ + { 0, 0 }, \ + { 0, 0 }, \ + { "ipsec", CTLTYPE_NODE }, \ } /* * Names for IP sysctl objects */ #define IPCTL_FORWARDING 1 /* act as router */ #define IPCTL_SENDREDIRECTS 2 /* may send redirects when forwarding */ #define IPCTL_DEFTTL 3 /* default TTL */ #ifdef notyet #define IPCTL_DEFMTU 4 /* default MTU */ #endif #define IPCTL_RTEXPIRE 5 /* cloned route expiration time */ #define IPCTL_RTMINEXPIRE 6 /* min value for expiration time */ #define IPCTL_RTMAXCACHE 7 /* trigger level for dynamic expire */ #define IPCTL_SOURCEROUTE 8 /* may perform source routes */ #define IPCTL_DIRECTEDBROADCAST 9 /* may re-broadcast received packets */ #define IPCTL_INTRQMAXLEN 10 /* max length of netisr queue */ #define IPCTL_INTRQDROPS 11 /* number of netisr q drops */ #define IPCTL_STATS 12 /* ipstat structure */ #define IPCTL_ACCEPTSOURCEROUTE 13 /* may accept source routed packets */ #define IPCTL_FASTFORWARDING 14 /* use fast IP forwarding code */ #define IPCTL_KEEPFAITH 15 /* FAITH IPv4->IPv6 translater ctl */ #define IPCTL_GIF_TTL 16 /* default TTL for gif encap packet */ #define IPCTL_MAXID 17 #define IPCTL_NAMES { \ { 0, 0 }, \ { "forwarding", CTLTYPE_INT }, \ { "redirect", CTLTYPE_INT }, \ { "ttl", CTLTYPE_INT }, \ { "mtu", CTLTYPE_INT }, \ { "rtexpire", CTLTYPE_INT }, \ { "rtminexpire", CTLTYPE_INT }, \ { "rtmaxcache", CTLTYPE_INT }, \ { "sourceroute", CTLTYPE_INT }, \ { "directed-broadcast", CTLTYPE_INT }, \ { "intr-queue-maxlen", CTLTYPE_INT }, \ { "intr-queue-drops", CTLTYPE_INT }, \ { "stats", CTLTYPE_STRUCT }, \ { "accept_sourceroute", CTLTYPE_INT }, \ { "fastforwarding", CTLTYPE_INT }, \ } /* INET6 stuff */ #define __KAME_NETINET_IN_H_INCLUDED_ #include #undef __KAME_NETINET_IN_H_INCLUDED_ #ifdef _KERNEL struct ifnet; struct mbuf; /* forward declarations for Standard C */ struct proc; int in_broadcast __P((struct in_addr, struct ifnet *)); int in_canforward __P((struct in_addr)); int in_localaddr __P((struct in_addr)); char *inet_ntoa __P((struct in_addr)); /* in libkern */ int prison_ip __P((struct proc *p, int flag, u_int32_t *ip)); void prison_remote_ip __P((struct proc *p, int flag, u_int32_t *ip)); #endif #endif Index: head/sys/netinet/in_gif.c =================================================================== --- head/sys/netinet/in_gif.c (revision 62586) +++ head/sys/netinet/in_gif.c (revision 62587) @@ -1,361 +1,387 @@ +/* $FreeBSD$ */ +/* $KAME: in_gif.c,v 1.43 2000/06/20 19:45:00 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ -/* - * in_gif.c - */ - #include "opt_mrouting.h" +#include "opt_inet.h" #include "opt_inet6.h" #include #include #include #include #include #include #include #include -#include +#include + #include #include #include #include #include -#ifdef INET6 -#include -#endif #include #include +#include +#include #include + #ifdef INET6 -#include +#include #endif #ifdef MROUTING #include #endif /* MROUTING */ -#include +#include #include "gif.h" #include #include #if NGIF > 0 int ip_gif_ttl = GIF_TTL; #else int ip_gif_ttl = 0; #endif - -SYSCTL_DECL(_net_inet_ip); SYSCTL_INT(_net_inet_ip, IPCTL_GIF_TTL, gifttl, CTLFLAG_RW, &ip_gif_ttl, 0, ""); -#define IN6_IS_ADDR_6TO4(x) (ntohs((x)->s6_addr16[0]) == 0x2002) -#define GET_V4(x) ((struct in_addr *)(&(x)->s6_addr16[1])) +#ifndef offsetof +#define offsetof(s, e) ((int)&((s *)0)->e) +#endif int in_gif_output(ifp, family, m, rt) struct ifnet *ifp; int family; struct mbuf *m; struct rtentry *rt; { register struct gif_softc *sc = (struct gif_softc*)ifp; struct sockaddr_in *dst = (struct sockaddr_in *)&sc->gif_ro.ro_dst; struct sockaddr_in *sin_src = (struct sockaddr_in *)sc->gif_psrc; struct sockaddr_in *sin_dst = (struct sockaddr_in *)sc->gif_pdst; struct ip iphdr; /* capsule IP header, host byte ordered */ int proto, error; u_int8_t tos; -#ifdef INET6 - struct ip6_hdr *ip6 = NULL; -#endif if (sin_src == NULL || sin_dst == NULL || sin_src->sin_family != AF_INET || sin_dst->sin_family != AF_INET) { m_freem(m); return EAFNOSUPPORT; } switch (family) { +#ifdef INET case AF_INET: { struct ip *ip; proto = IPPROTO_IPV4; if (m->m_len < sizeof(*ip)) { m = m_pullup(m, sizeof(*ip)); if (!m) return ENOBUFS; } ip = mtod(m, struct ip *); tos = ip->ip_tos; break; } +#endif /*INET*/ #ifdef INET6 case AF_INET6: { + struct ip6_hdr *ip6; proto = IPPROTO_IPV6; if (m->m_len < sizeof(*ip6)) { m = m_pullup(m, sizeof(*ip6)); if (!m) return ENOBUFS; } ip6 = mtod(m, struct ip6_hdr *); tos = (ntohl(ip6->ip6_flow) >> 20) & 0xff; break; } #endif /*INET6*/ default: -#ifdef DIAGNOSTIC +#ifdef DEBUG printf("in_gif_output: warning: unknown family %d passed\n", family); #endif m_freem(m); return EAFNOSUPPORT; } bzero(&iphdr, sizeof(iphdr)); iphdr.ip_src = sin_src->sin_addr; -#ifdef INET6 - /* XXX: temporal stf support hack */ - if (bcmp(ifp->if_name, "stf", 3) == 0) { - if (ip6 == NULL) { - m_freem(m); - return ENETUNREACH; - } - if (IN6_IS_ADDR_6TO4(&ip6->ip6_dst)) - iphdr.ip_dst = *GET_V4(&ip6->ip6_dst); - else if (rt && rt->rt_gateway->sa_family == AF_INET6) { - struct in6_addr *dst6; - - dst6 = &((struct sockaddr_in6 *) - (rt->rt_gateway))->sin6_addr; - if (IN6_IS_ADDR_6TO4(dst6)) - iphdr.ip_dst = *GET_V4(dst6); - else { - m_freem(m); - return ENETUNREACH; - } - } else { - m_freem(m); - return ENETUNREACH; - } - } else -#endif if (ifp->if_flags & IFF_LINK0) { /* multi-destination mode */ if (sin_dst->sin_addr.s_addr != INADDR_ANY) iphdr.ip_dst = sin_dst->sin_addr; else if (rt) { + if (family != AF_INET) { + m_freem(m); + return EINVAL; /*XXX*/ + } iphdr.ip_dst = ((struct sockaddr_in *) (rt->rt_gateway))->sin_addr; } else { m_freem(m); return ENETUNREACH; } } else { /* bidirectional configured tunnel mode */ if (sin_dst->sin_addr.s_addr != INADDR_ANY) iphdr.ip_dst = sin_dst->sin_addr; else { m_freem(m); return ENETUNREACH; } } iphdr.ip_p = proto; /* version will be set in ip_output() */ iphdr.ip_ttl = ip_gif_ttl; iphdr.ip_len = m->m_pkthdr.len + sizeof(struct ip); if (ifp->if_flags & IFF_LINK1) ip_ecn_ingress(ECN_ALLOWED, &iphdr.ip_tos, &tos); /* prepend new IP header */ M_PREPEND(m, sizeof(struct ip), M_DONTWAIT); if (m && m->m_len < sizeof(struct ip)) m = m_pullup(m, sizeof(struct ip)); if (m == NULL) { printf("ENOBUFS in in_gif_output %d\n", __LINE__); return ENOBUFS; } + bcopy(&iphdr, mtod(m, struct ip *), sizeof(struct ip)); - *(mtod(m, struct ip *)) = iphdr; - if (dst->sin_family != sin_dst->sin_family || dst->sin_addr.s_addr != sin_dst->sin_addr.s_addr) { /* cache route doesn't match */ dst->sin_family = sin_dst->sin_family; dst->sin_len = sizeof(struct sockaddr_in); dst->sin_addr = sin_dst->sin_addr; if (sc->gif_ro.ro_rt) { RTFREE(sc->gif_ro.ro_rt); sc->gif_ro.ro_rt = NULL; } +#if 0 + sc->gif_if.if_mtu = GIF_MTU; +#endif } if (sc->gif_ro.ro_rt == NULL) { rtalloc(&sc->gif_ro); if (sc->gif_ro.ro_rt == NULL) { m_freem(m); return ENETUNREACH; } + + /* if it constitutes infinite encapsulation, punt. */ + if (sc->gif_ro.ro_rt->rt_ifp == ifp) { + m_freem(m); + return ENETUNREACH; /*XXX*/ + } +#if 0 + ifp->if_mtu = sc->gif_ro.ro_rt->rt_ifp->if_mtu + - sizeof(struct ip); +#endif } - error = ip_output(m, 0, &sc->gif_ro, 0, 0); + error = ip_output(m, NULL, &sc->gif_ro, 0, NULL); return(error); } void -in_gif_input(struct mbuf *m, int off, int proto) +#if __STDC__ +in_gif_input(struct mbuf *m, ...) +#else +in_gif_input(m, va_alist) + struct mbuf *m; + va_dcl +#endif { - struct gif_softc *sc; + int off, proto; struct ifnet *gifp = NULL; struct ip *ip; - int i, af; + va_list ap; + int af; u_int8_t otos; + va_start(ap, m); + off = va_arg(ap, int); + proto = va_arg(ap, int); + va_end(ap); + ip = mtod(m, struct ip *); - /* this code will be soon improved. */ -#define satosin(sa) ((struct sockaddr_in *)(sa)) - for (i = 0, sc = gif; i < ngif; i++, sc++) { - if (sc->gif_psrc == NULL - || sc->gif_pdst == NULL - || sc->gif_psrc->sa_family != AF_INET - || sc->gif_pdst->sa_family != AF_INET) { - continue; - } + gifp = (struct ifnet *)encap_getarg(m); - if ((sc->gif_if.if_flags & IFF_UP) == 0) - continue; - -#ifdef INET6 - /* XXX: temporal stf support hack */ - if (proto == IPPROTO_IPV6 && - bcmp(sc->gif_if.if_name, "stf", 3) == 0 && - (satosin(sc->gif_psrc)->sin_addr.s_addr == - ip->ip_dst.s_addr || - IN_MULTICAST(ntohl(ip->ip_dst.s_addr))) && - satosin(sc->gif_pdst)->sin_addr.s_addr == - INADDR_BROADCAST) { - gifp = &sc->gif_if; - break; - } -#endif - - if ((sc->gif_if.if_flags & IFF_LINK0) - && satosin(sc->gif_psrc)->sin_addr.s_addr == ip->ip_dst.s_addr - && satosin(sc->gif_pdst)->sin_addr.s_addr == INADDR_ANY) { - gifp = &sc->gif_if; - continue; - } - - if (satosin(sc->gif_psrc)->sin_addr.s_addr == ip->ip_dst.s_addr - && satosin(sc->gif_pdst)->sin_addr.s_addr == ip->ip_src.s_addr) - { - gifp = &sc->gif_if; - break; - } - } - - if (gifp == NULL) { -#ifdef MROUTING - /* for backward compatibility */ - if (proto == IPPROTO_IPV4) { - ipip_input(m, off, proto); - return; - } -#endif /*MROUTING*/ + if (gifp == NULL || (gifp->if_flags & IFF_UP) == 0) { m_freem(m); ipstat.ips_nogif++; return; } otos = ip->ip_tos; m_adj(m, off); switch (proto) { +#ifdef INET case IPPROTO_IPV4: { struct ip *ip; - -#ifdef INET6 - if (bcmp(gifp->if_name, "stf", 3) == 0) { - m_freem(m); - return; - } -#endif af = AF_INET; if (m->m_len < sizeof(*ip)) { m = m_pullup(m, sizeof(*ip)); if (!m) return; } ip = mtod(m, struct ip *); if (gifp->if_flags & IFF_LINK1) ip_ecn_egress(ECN_ALLOWED, &otos, &ip->ip_tos); break; } +#endif #ifdef INET6 case IPPROTO_IPV6: { struct ip6_hdr *ip6; u_int8_t itos; af = AF_INET6; if (m->m_len < sizeof(*ip6)) { m = m_pullup(m, sizeof(*ip6)); if (!m) return; } ip6 = mtod(m, struct ip6_hdr *); itos = (ntohl(ip6->ip6_flow) >> 20) & 0xff; if (gifp->if_flags & IFF_LINK1) ip_ecn_egress(ECN_ALLOWED, &otos, &itos); ip6->ip6_flow &= ~htonl(0xff << 20); ip6->ip6_flow |= htonl((u_int32_t)itos << 20); break; } #endif /* INET6 */ default: ipstat.ips_nogif++; m_freem(m); return; } gif_input(m, af, gifp); return; +} + +/* + * we know that we are in IFF_UP, outer address available, and outer family + * matched the physical addr family. see gif_encapcheck(). + */ +int +gif_encapcheck4(m, off, proto, arg) + const struct mbuf *m; + int off; + int proto; + void *arg; +{ + struct ip ip; + struct gif_softc *sc; + struct sockaddr_in *src, *dst; + int addrmatch; + struct in_ifaddr *ia4; + + /* sanity check done in caller */ + sc = (struct gif_softc *)arg; + src = (struct sockaddr_in *)sc->gif_psrc; + dst = (struct sockaddr_in *)sc->gif_pdst; + + /* LINTED const cast */ + m_copydata((struct mbuf *)m, 0, sizeof(ip), (caddr_t)&ip); + + /* check for address match */ + addrmatch = 0; + if (src->sin_addr.s_addr == ip.ip_dst.s_addr) + addrmatch |= 1; + if (dst->sin_addr.s_addr == ip.ip_src.s_addr) + addrmatch |= 2; + else if ((sc->gif_if.if_flags & IFF_LINK0) != 0 && + dst->sin_addr.s_addr == INADDR_ANY) { + addrmatch |= 2; /* we accept any source */ + } + if (addrmatch != 3) + return 0; + + /* martian filters on outer source - NOT done in ip_input! */ + if (IN_MULTICAST(ip.ip_src.s_addr)) + return 0; + switch ((ntohl(ip.ip_src.s_addr) & 0xff000000) >> 24) { + case 0: case 127: case 255: + return 0; + } + /* reject packets with broadcast on source */ + for (ia4 = TAILQ_FIRST(&in_ifaddrhead); ia4; + ia4 = TAILQ_NEXT(ia4, ia_link)) + { + if ((ia4->ia_ifa.ifa_ifp->if_flags & IFF_BROADCAST) == 0) + continue; + if (ip.ip_src.s_addr == ia4->ia_broadaddr.sin_addr.s_addr) + return 0; + } + + /* ingress filters on outer source */ + if ((m->m_flags & M_PKTHDR) != 0 && m->m_pkthdr.rcvif) { + struct sockaddr_in sin; + struct rtentry *rt; + + bzero(&sin, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_len = sizeof(struct sockaddr_in); + sin.sin_addr = ip.ip_src; + rt = rtalloc1((struct sockaddr *)&sin, 0, 0UL); + if (!rt) + return 0; + if (rt->rt_ifp != m->m_pkthdr.rcvif) { + rtfree(rt); + return 0; + } + rtfree(rt); + } + + /* prioritize: IFF_LINK0 mode is less preferred */ + return (sc->gif_if.if_flags & IFF_LINK0) ? 32 : 32 * 2; } Index: head/sys/netinet/in_gif.h =================================================================== --- head/sys/netinet/in_gif.h (revision 62586) +++ head/sys/netinet/in_gif.h (revision 62587) @@ -1,47 +1,44 @@ +/* $FreeBSD$ */ +/* $KAME: in_gif.h,v 1.5 2000/04/14 08:36:02 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ #ifndef _NETINET_IN_GIF_H_ -#define _NETINET_IN_GIF_H_ +#define _NETINET_IN_GIF_H_ -#define GIF_TTL 30 +#define GIF_TTL 30 extern int ip_gif_ttl; -struct mbuf; -struct ifnet; -struct rtentry; - -void in_gif_input __P((struct mbuf *, int off, int proto)); -int in_gif_output __P((struct ifnet *, int, struct mbuf *, - struct rtentry *)); +void in_gif_input __P((struct mbuf *, ...)); +int in_gif_output __P((struct ifnet *, int, struct mbuf *, struct rtentry *)); +int gif_encapcheck4 __P((const struct mbuf *, int, int, void *)); #endif /*_NETINET_IN_GIF_H_*/ Index: head/sys/netinet/in_pcb.c =================================================================== --- head/sys/netinet/in_pcb.c (revision 62586) +++ head/sys/netinet/in_pcb.c (revision 62587) @@ -1,1027 +1,1025 @@ /* * Copyright (c) 1982, 1986, 1991, 1993, 1995 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)in_pcb.c 8.4 (Berkeley) 5/24/95 * $FreeBSD$ */ #include "opt_ipsec.h" #include "opt_inet6.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef INET6 #include #include #endif /* INET6 */ #include "faith.h" #ifdef IPSEC #include #include -#include #endif /* IPSEC */ struct in_addr zeroin_addr; static void in_rtchange __P((struct inpcb *, int)); /* * These configure the range of local port addresses assigned to * "unspecified" outgoing connections/packets/whatever. */ int ipport_lowfirstauto = IPPORT_RESERVED - 1; /* 1023 */ int ipport_lowlastauto = IPPORT_RESERVEDSTART; /* 600 */ int ipport_firstauto = IPPORT_RESERVED; /* 1024 */ int ipport_lastauto = IPPORT_USERRESERVED; /* 5000 */ int ipport_hifirstauto = IPPORT_HIFIRSTAUTO; /* 49152 */ int ipport_hilastauto = IPPORT_HILASTAUTO; /* 65535 */ #define RANGECHK(var, min, max) \ if ((var) < (min)) { (var) = (min); } \ else if ((var) > (max)) { (var) = (max); } static int sysctl_net_ipport_check(SYSCTL_HANDLER_ARGS) { int error = sysctl_handle_int(oidp, oidp->oid_arg1, oidp->oid_arg2, req); if (!error) { RANGECHK(ipport_lowfirstauto, 1, IPPORT_RESERVED - 1); RANGECHK(ipport_lowlastauto, 1, IPPORT_RESERVED - 1); RANGECHK(ipport_firstauto, IPPORT_RESERVED, USHRT_MAX); RANGECHK(ipport_lastauto, IPPORT_RESERVED, USHRT_MAX); RANGECHK(ipport_hifirstauto, IPPORT_RESERVED, USHRT_MAX); RANGECHK(ipport_hilastauto, IPPORT_RESERVED, USHRT_MAX); } return error; } #undef RANGECHK SYSCTL_NODE(_net_inet_ip, IPPROTO_IP, portrange, CTLFLAG_RW, 0, "IP Ports"); SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, lowfirst, CTLTYPE_INT|CTLFLAG_RW, &ipport_lowfirstauto, 0, &sysctl_net_ipport_check, "I", ""); SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, lowlast, CTLTYPE_INT|CTLFLAG_RW, &ipport_lowlastauto, 0, &sysctl_net_ipport_check, "I", ""); SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, first, CTLTYPE_INT|CTLFLAG_RW, &ipport_firstauto, 0, &sysctl_net_ipport_check, "I", ""); SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, last, CTLTYPE_INT|CTLFLAG_RW, &ipport_lastauto, 0, &sysctl_net_ipport_check, "I", ""); SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, hifirst, CTLTYPE_INT|CTLFLAG_RW, &ipport_hifirstauto, 0, &sysctl_net_ipport_check, "I", ""); SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, hilast, CTLTYPE_INT|CTLFLAG_RW, &ipport_hilastauto, 0, &sysctl_net_ipport_check, "I", ""); /* * in_pcb.c: manage the Protocol Control Blocks. * * NOTE: It is assumed that most of these functions will be called at * splnet(). XXX - There are, unfortunately, a few exceptions to this * rule that should be fixed. */ /* * Allocate a PCB and associate it with the socket. */ int in_pcballoc(so, pcbinfo, p) struct socket *so; struct inpcbinfo *pcbinfo; struct proc *p; { register struct inpcb *inp; inp = zalloci(pcbinfo->ipi_zone); if (inp == NULL) return (ENOBUFS); bzero((caddr_t)inp, sizeof(*inp)); inp->inp_gencnt = ++pcbinfo->ipi_gencnt; inp->inp_pcbinfo = pcbinfo; inp->inp_socket = so; #if defined(INET6) if (ip6_mapped_addr_on) inp->inp_flags &= ~IN6P_BINDV6ONLY; else inp->inp_flags |= IN6P_BINDV6ONLY; #endif LIST_INSERT_HEAD(pcbinfo->listhead, inp, inp_list); pcbinfo->ipi_count++; so->so_pcb = (caddr_t)inp; return (0); } int in_pcbbind(inp, nam, p) register struct inpcb *inp; struct sockaddr *nam; struct proc *p; { register struct socket *so = inp->inp_socket; unsigned short *lastport; struct sockaddr_in *sin; struct inpcbinfo *pcbinfo = inp->inp_pcbinfo; u_short lport = 0; int wild = 0, reuseport = (so->so_options & SO_REUSEPORT); int error, prison = 0; if (TAILQ_EMPTY(&in_ifaddrhead)) /* XXX broken! */ return (EADDRNOTAVAIL); if (inp->inp_lport || inp->inp_laddr.s_addr != INADDR_ANY) return (EINVAL); if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0) wild = 1; if (nam) { sin = (struct sockaddr_in *)nam; if (nam->sa_len != sizeof (*sin)) return (EINVAL); #ifdef notdef /* * We should check the family, but old programs * incorrectly fail to initialize it. */ if (sin->sin_family != AF_INET) return (EAFNOSUPPORT); #endif if (prison_ip(p, 0, &sin->sin_addr.s_addr)) return(EINVAL); lport = sin->sin_port; if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr))) { /* * Treat SO_REUSEADDR as SO_REUSEPORT for multicast; * allow complete duplication of binding if * SO_REUSEPORT is set, or if SO_REUSEADDR is set * and a multicast address is bound on both * new and duplicated sockets. */ if (so->so_options & SO_REUSEADDR) reuseport = SO_REUSEADDR|SO_REUSEPORT; } else if (sin->sin_addr.s_addr != INADDR_ANY) { sin->sin_port = 0; /* yech... */ if (ifa_ifwithaddr((struct sockaddr *)sin) == 0) return (EADDRNOTAVAIL); } if (lport) { struct inpcb *t; /* GROSS */ if (ntohs(lport) < IPPORT_RESERVED && p && suser_xxx(0, p, PRISON_ROOT)) return (EACCES); if (p && p->p_prison) prison = 1; if (so->so_cred->cr_uid != 0 && !IN_MULTICAST(ntohl(sin->sin_addr.s_addr))) { t = in_pcblookup_local(inp->inp_pcbinfo, sin->sin_addr, lport, prison ? 0 : INPLOOKUP_WILDCARD); if (t && (ntohl(sin->sin_addr.s_addr) != INADDR_ANY || ntohl(t->inp_laddr.s_addr) != INADDR_ANY || (t->inp_socket->so_options & SO_REUSEPORT) == 0) && (so->so_cred->cr_uid != t->inp_socket->so_cred->cr_uid)) { #if defined(INET6) if ((inp->inp_flags & IN6P_BINDV6ONLY) != 0 || ntohl(sin->sin_addr.s_addr) != INADDR_ANY || ntohl(t->inp_laddr.s_addr) != INADDR_ANY || INP_SOCKAF(so) == INP_SOCKAF(t->inp_socket)) #endif /* defined(INET6) */ return (EADDRINUSE); } } t = in_pcblookup_local(pcbinfo, sin->sin_addr, lport, prison ? 0 : wild); if (t && (reuseport & t->inp_socket->so_options) == 0) { #if defined(INET6) if ((inp->inp_flags & IN6P_BINDV6ONLY) != 0 || ntohl(sin->sin_addr.s_addr) != INADDR_ANY || ntohl(t->inp_laddr.s_addr) != INADDR_ANY || INP_SOCKAF(so) == INP_SOCKAF(t->inp_socket)) #endif /* defined(INET6) */ return (EADDRINUSE); } } inp->inp_laddr = sin->sin_addr; } if (lport == 0) { ushort first, last; int count; if (prison_ip(p, 0, &inp->inp_laddr.s_addr )) return (EINVAL); inp->inp_flags |= INP_ANONPORT; if (inp->inp_flags & INP_HIGHPORT) { first = ipport_hifirstauto; /* sysctl */ last = ipport_hilastauto; lastport = &pcbinfo->lasthi; } else if (inp->inp_flags & INP_LOWPORT) { if (p && (error = suser_xxx(0, p, PRISON_ROOT))) return error; first = ipport_lowfirstauto; /* 1023 */ last = ipport_lowlastauto; /* 600 */ lastport = &pcbinfo->lastlow; } else { first = ipport_firstauto; /* sysctl */ last = ipport_lastauto; lastport = &pcbinfo->lastport; } /* * Simple check to ensure all ports are not used up causing * a deadlock here. * * We split the two cases (up and down) so that the direction * is not being tested on each round of the loop. */ if (first > last) { /* * counting down */ count = first - last; do { if (count-- < 0) { /* completely used? */ /* * Undo any address bind that may have * occurred above. */ inp->inp_laddr.s_addr = INADDR_ANY; return (EAGAIN); } --*lastport; if (*lastport > first || *lastport < last) *lastport = first; lport = htons(*lastport); } while (in_pcblookup_local(pcbinfo, inp->inp_laddr, lport, wild)); } else { /* * counting up */ count = last - first; do { if (count-- < 0) { /* completely used? */ /* * Undo any address bind that may have * occurred above. */ inp->inp_laddr.s_addr = INADDR_ANY; return (EAGAIN); } ++*lastport; if (*lastport < first || *lastport > last) *lastport = first; lport = htons(*lastport); } while (in_pcblookup_local(pcbinfo, inp->inp_laddr, lport, wild)); } } inp->inp_lport = lport; if (in_pcbinshash(inp) != 0) { inp->inp_laddr.s_addr = INADDR_ANY; inp->inp_lport = 0; return (EAGAIN); } return (0); } /* * Transform old in_pcbconnect() into an inner subroutine for new * in_pcbconnect(): Do some validity-checking on the remote * address (in mbuf 'nam') and then determine local host address * (i.e., which interface) to use to access that remote host. * * This preserves definition of in_pcbconnect(), while supporting a * slightly different version for T/TCP. (This is more than * a bit of a kludge, but cleaning up the internal interfaces would * have forced minor changes in every protocol). */ int in_pcbladdr(inp, nam, plocal_sin) register struct inpcb *inp; struct sockaddr *nam; struct sockaddr_in **plocal_sin; { struct in_ifaddr *ia; register struct sockaddr_in *sin = (struct sockaddr_in *)nam; if (nam->sa_len != sizeof (*sin)) return (EINVAL); if (sin->sin_family != AF_INET) return (EAFNOSUPPORT); if (sin->sin_port == 0) return (EADDRNOTAVAIL); if (!TAILQ_EMPTY(&in_ifaddrhead)) { /* * If the destination address is INADDR_ANY, * use the primary local address. * If the supplied address is INADDR_BROADCAST, * and the primary interface supports broadcast, * choose the broadcast address for that interface. */ #define satosin(sa) ((struct sockaddr_in *)(sa)) #define sintosa(sin) ((struct sockaddr *)(sin)) #define ifatoia(ifa) ((struct in_ifaddr *)(ifa)) if (sin->sin_addr.s_addr == INADDR_ANY) sin->sin_addr = IA_SIN(in_ifaddrhead.tqh_first)->sin_addr; else if (sin->sin_addr.s_addr == (u_long)INADDR_BROADCAST && (in_ifaddrhead.tqh_first->ia_ifp->if_flags & IFF_BROADCAST)) sin->sin_addr = satosin(&in_ifaddrhead.tqh_first->ia_broadaddr)->sin_addr; } if (inp->inp_laddr.s_addr == INADDR_ANY) { register struct route *ro; ia = (struct in_ifaddr *)0; /* * If route is known or can be allocated now, * our src addr is taken from the i/f, else punt. */ ro = &inp->inp_route; if (ro->ro_rt && (satosin(&ro->ro_dst)->sin_addr.s_addr != sin->sin_addr.s_addr || inp->inp_socket->so_options & SO_DONTROUTE)) { RTFREE(ro->ro_rt); ro->ro_rt = (struct rtentry *)0; } if ((inp->inp_socket->so_options & SO_DONTROUTE) == 0 && /*XXX*/ (ro->ro_rt == (struct rtentry *)0 || ro->ro_rt->rt_ifp == (struct ifnet *)0)) { /* No route yet, so try to acquire one */ ro->ro_dst.sa_family = AF_INET; ro->ro_dst.sa_len = sizeof(struct sockaddr_in); ((struct sockaddr_in *) &ro->ro_dst)->sin_addr = sin->sin_addr; rtalloc(ro); } /* * If we found a route, use the address * corresponding to the outgoing interface * unless it is the loopback (in case a route * to our address on another net goes to loopback). */ if (ro->ro_rt && !(ro->ro_rt->rt_ifp->if_flags & IFF_LOOPBACK)) ia = ifatoia(ro->ro_rt->rt_ifa); if (ia == 0) { u_short fport = sin->sin_port; sin->sin_port = 0; ia = ifatoia(ifa_ifwithdstaddr(sintosa(sin))); if (ia == 0) ia = ifatoia(ifa_ifwithnet(sintosa(sin))); sin->sin_port = fport; if (ia == 0) ia = in_ifaddrhead.tqh_first; if (ia == 0) return (EADDRNOTAVAIL); } /* * If the destination address is multicast and an outgoing * interface has been set as a multicast option, use the * address of that interface as our source address. */ if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr)) && inp->inp_moptions != NULL) { struct ip_moptions *imo; struct ifnet *ifp; imo = inp->inp_moptions; if (imo->imo_multicast_ifp != NULL) { ifp = imo->imo_multicast_ifp; for (ia = in_ifaddrhead.tqh_first; ia; ia = ia->ia_link.tqe_next) if (ia->ia_ifp == ifp) break; if (ia == 0) return (EADDRNOTAVAIL); } } /* * Don't do pcblookup call here; return interface in plocal_sin * and exit to caller, that will do the lookup. */ *plocal_sin = &ia->ia_addr; } return(0); } /* * Outer subroutine: * Connect from a socket to a specified address. * Both address and port must be specified in argument sin. * If don't have a local address for this socket yet, * then pick one. */ int in_pcbconnect(inp, nam, p) register struct inpcb *inp; struct sockaddr *nam; struct proc *p; { struct sockaddr_in *ifaddr; register struct sockaddr_in *sin = (struct sockaddr_in *)nam; int error; /* * Call inner routine, to assign local interface address. */ if ((error = in_pcbladdr(inp, nam, &ifaddr)) != 0) return(error); if (in_pcblookup_hash(inp->inp_pcbinfo, sin->sin_addr, sin->sin_port, inp->inp_laddr.s_addr ? inp->inp_laddr : ifaddr->sin_addr, inp->inp_lport, 0, NULL) != NULL) { return (EADDRINUSE); } if (inp->inp_laddr.s_addr == INADDR_ANY) { if (inp->inp_lport == 0) { error = in_pcbbind(inp, (struct sockaddr *)0, p); if (error) return (error); } inp->inp_laddr = ifaddr->sin_addr; } inp->inp_faddr = sin->sin_addr; inp->inp_fport = sin->sin_port; in_pcbrehash(inp); return (0); } void in_pcbdisconnect(inp) struct inpcb *inp; { inp->inp_faddr.s_addr = INADDR_ANY; inp->inp_fport = 0; in_pcbrehash(inp); if (inp->inp_socket->so_state & SS_NOFDREF) in_pcbdetach(inp); } void in_pcbdetach(inp) struct inpcb *inp; { struct socket *so = inp->inp_socket; struct inpcbinfo *ipi = inp->inp_pcbinfo; #ifdef IPSEC - if (inp->inp_sp != NULL) - ipsec4_delete_pcbpolicy(inp); + ipsec4_delete_pcbpolicy(inp); #endif /*IPSEC*/ inp->inp_gencnt = ++ipi->ipi_gencnt; in_pcbremlists(inp); so->so_pcb = 0; sofree(so); if (inp->inp_options) (void)m_free(inp->inp_options); if (inp->inp_route.ro_rt) rtfree(inp->inp_route.ro_rt); ip_freemoptions(inp->inp_moptions); inp->inp_vflag = 0; zfreei(ipi->ipi_zone, inp); } /* * The calling convention of in_setsockaddr() and in_setpeeraddr() was * modified to match the pru_sockaddr() and pru_peeraddr() entry points * in struct pr_usrreqs, so that protocols can just reference then directly * without the need for a wrapper function. The socket must have a valid * (i.e., non-nil) PCB, but it should be impossible to get an invalid one * except through a kernel programming error, so it is acceptable to panic * (or in this case trap) if the PCB is invalid. (Actually, we don't trap * because there actually /is/ a programming error somewhere... XXX) */ int in_setsockaddr(so, nam) struct socket *so; struct sockaddr **nam; { int s; register struct inpcb *inp; register struct sockaddr_in *sin; /* * Do the malloc first in case it blocks. */ MALLOC(sin, struct sockaddr_in *, sizeof *sin, M_SONAME, M_WAITOK); bzero(sin, sizeof *sin); sin->sin_family = AF_INET; sin->sin_len = sizeof(*sin); s = splnet(); inp = sotoinpcb(so); if (!inp) { splx(s); free(sin, M_SONAME); return ECONNRESET; } sin->sin_port = inp->inp_lport; sin->sin_addr = inp->inp_laddr; splx(s); *nam = (struct sockaddr *)sin; return 0; } int in_setpeeraddr(so, nam) struct socket *so; struct sockaddr **nam; { int s; struct inpcb *inp; register struct sockaddr_in *sin; /* * Do the malloc first in case it blocks. */ MALLOC(sin, struct sockaddr_in *, sizeof *sin, M_SONAME, M_WAITOK); bzero(sin, sizeof (*sin)); sin->sin_family = AF_INET; sin->sin_len = sizeof(*sin); s = splnet(); inp = sotoinpcb(so); if (!inp) { splx(s); free(sin, M_SONAME); return ECONNRESET; } sin->sin_port = inp->inp_fport; sin->sin_addr = inp->inp_faddr; splx(s); *nam = (struct sockaddr *)sin; return 0; } /* * Pass some notification to all connections of a protocol * associated with address dst. The local address and/or port numbers * may be specified to limit the search. The "usual action" will be * taken, depending on the ctlinput cmd. The caller must filter any * cmds that are uninteresting (e.g., no error in the map). * Call the protocol specific routine (if any) to report * any errors for each matching socket. */ void in_pcbnotify(head, dst, fport_arg, laddr, lport_arg, cmd, notify) struct inpcbhead *head; struct sockaddr *dst; u_int fport_arg, lport_arg; struct in_addr laddr; int cmd; void (*notify) __P((struct inpcb *, int)); { register struct inpcb *inp, *oinp; struct in_addr faddr; u_short fport = fport_arg, lport = lport_arg; int errno, s; if ((unsigned)cmd > PRC_NCMDS || dst->sa_family != AF_INET) return; faddr = ((struct sockaddr_in *)dst)->sin_addr; if (faddr.s_addr == INADDR_ANY) return; /* * Redirects go to all references to the destination, * and use in_rtchange to invalidate the route cache. * Dead host indications: notify all references to the destination. * Otherwise, if we have knowledge of the local port and address, * deliver only to that socket. */ if (PRC_IS_REDIRECT(cmd) || cmd == PRC_HOSTDEAD) { fport = 0; lport = 0; laddr.s_addr = 0; if (cmd != PRC_HOSTDEAD) notify = in_rtchange; } errno = inetctlerrmap[cmd]; s = splnet(); for (inp = head->lh_first; inp != NULL;) { #ifdef INET6 if ((inp->inp_vflag & INP_IPV4) == 0) { inp = LIST_NEXT(inp, inp_list); continue; } #endif if (inp->inp_faddr.s_addr != faddr.s_addr || inp->inp_socket == 0 || (lport && inp->inp_lport != lport) || (laddr.s_addr && inp->inp_laddr.s_addr != laddr.s_addr) || (fport && inp->inp_fport != fport)) { inp = inp->inp_list.le_next; continue; } oinp = inp; inp = inp->inp_list.le_next; if (notify) (*notify)(oinp, errno); } splx(s); } /* * Check for alternatives when higher level complains * about service problems. For now, invalidate cached * routing information. If the route was created dynamically * (by a redirect), time to try a default gateway again. */ void in_losing(inp) struct inpcb *inp; { register struct rtentry *rt; struct rt_addrinfo info; if ((rt = inp->inp_route.ro_rt)) { inp->inp_route.ro_rt = 0; bzero((caddr_t)&info, sizeof(info)); info.rti_info[RTAX_DST] = (struct sockaddr *)&inp->inp_route.ro_dst; info.rti_info[RTAX_GATEWAY] = rt->rt_gateway; info.rti_info[RTAX_NETMASK] = rt_mask(rt); rt_missmsg(RTM_LOSING, &info, rt->rt_flags, 0); if (rt->rt_flags & RTF_DYNAMIC) (void) rtrequest(RTM_DELETE, rt_key(rt), rt->rt_gateway, rt_mask(rt), rt->rt_flags, (struct rtentry **)0); else /* * A new route can be allocated * the next time output is attempted. */ rtfree(rt); } } /* * After a routing change, flush old routing * and allocate a (hopefully) better one. */ static void in_rtchange(inp, errno) register struct inpcb *inp; int errno; { if (inp->inp_route.ro_rt) { rtfree(inp->inp_route.ro_rt); inp->inp_route.ro_rt = 0; /* * A new route can be allocated the next time * output is attempted. */ } } /* * Lookup a PCB based on the local address and port. */ struct inpcb * in_pcblookup_local(pcbinfo, laddr, lport_arg, wild_okay) struct inpcbinfo *pcbinfo; struct in_addr laddr; u_int lport_arg; int wild_okay; { register struct inpcb *inp; int matchwild = 3, wildcard; u_short lport = lport_arg; if (!wild_okay) { struct inpcbhead *head; /* * Look for an unconnected (wildcard foreign addr) PCB that * matches the local address and port we're looking for. */ head = &pcbinfo->hashbase[INP_PCBHASH(INADDR_ANY, lport, 0, pcbinfo->hashmask)]; for (inp = head->lh_first; inp != NULL; inp = inp->inp_hash.le_next) { #ifdef INET6 if ((inp->inp_vflag & INP_IPV4) == 0) continue; #endif if (inp->inp_faddr.s_addr == INADDR_ANY && inp->inp_laddr.s_addr == laddr.s_addr && inp->inp_lport == lport) { /* * Found. */ return (inp); } } /* * Not found. */ return (NULL); } else { struct inpcbporthead *porthash; struct inpcbport *phd; struct inpcb *match = NULL; /* * Best fit PCB lookup. * * First see if this local port is in use by looking on the * port hash list. */ porthash = &pcbinfo->porthashbase[INP_PCBPORTHASH(lport, pcbinfo->porthashmask)]; for (phd = porthash->lh_first; phd != NULL; phd = phd->phd_hash.le_next) { if (phd->phd_port == lport) break; } if (phd != NULL) { /* * Port is in use by one or more PCBs. Look for best * fit. */ for (inp = phd->phd_pcblist.lh_first; inp != NULL; inp = inp->inp_portlist.le_next) { wildcard = 0; #ifdef INET6 if ((inp->inp_vflag & INP_IPV4) == 0) continue; #endif if (inp->inp_faddr.s_addr != INADDR_ANY) wildcard++; if (inp->inp_laddr.s_addr != INADDR_ANY) { if (laddr.s_addr == INADDR_ANY) wildcard++; else if (inp->inp_laddr.s_addr != laddr.s_addr) continue; } else { if (laddr.s_addr != INADDR_ANY) wildcard++; } if (wildcard < matchwild) { match = inp; matchwild = wildcard; if (matchwild == 0) { break; } } } } return (match); } } /* * Lookup PCB in hash list. */ struct inpcb * in_pcblookup_hash(pcbinfo, faddr, fport_arg, laddr, lport_arg, wildcard, ifp) struct inpcbinfo *pcbinfo; struct in_addr faddr, laddr; u_int fport_arg, lport_arg; int wildcard; struct ifnet *ifp; { struct inpcbhead *head; register struct inpcb *inp; u_short fport = fport_arg, lport = lport_arg; /* * First look for an exact match. */ head = &pcbinfo->hashbase[INP_PCBHASH(faddr.s_addr, lport, fport, pcbinfo->hashmask)]; for (inp = head->lh_first; inp != NULL; inp = inp->inp_hash.le_next) { #ifdef INET6 if ((inp->inp_vflag & INP_IPV4) == 0) continue; #endif if (inp->inp_faddr.s_addr == faddr.s_addr && inp->inp_laddr.s_addr == laddr.s_addr && inp->inp_fport == fport && inp->inp_lport == lport) { /* * Found. */ return (inp); } } if (wildcard) { struct inpcb *local_wild = NULL; #if defined(INET6) struct inpcb *local_wild_mapped = NULL; #endif /* defined(INET6) */ head = &pcbinfo->hashbase[INP_PCBHASH(INADDR_ANY, lport, 0, pcbinfo->hashmask)]; for (inp = head->lh_first; inp != NULL; inp = inp->inp_hash.le_next) { #ifdef INET6 if ((inp->inp_vflag & INP_IPV4) == 0) continue; #endif if (inp->inp_faddr.s_addr == INADDR_ANY && inp->inp_lport == lport) { #if defined(NFAITH) && NFAITH > 0 if (ifp && ifp->if_type == IFT_FAITH && (inp->inp_flags & INP_FAITH) == 0) continue; #endif if (inp->inp_laddr.s_addr == laddr.s_addr) return (inp); else if (inp->inp_laddr.s_addr == INADDR_ANY) { #if defined(INET6) if (INP_CHECK_SOCKAF(inp->inp_socket, AF_INET6)) local_wild_mapped = inp; else #endif /* defined(INET6) */ local_wild = inp; } } } #if defined(INET6) if (local_wild == NULL) return (local_wild_mapped); #endif /* defined(INET6) */ return (local_wild); } /* * Not found. */ return (NULL); } /* * Insert PCB onto various hash lists. */ int in_pcbinshash(inp) struct inpcb *inp; { struct inpcbhead *pcbhash; struct inpcbporthead *pcbporthash; struct inpcbinfo *pcbinfo = inp->inp_pcbinfo; struct inpcbport *phd; u_int32_t hashkey_faddr; #ifdef INET6 if (inp->inp_vflag & INP_IPV6) hashkey_faddr = inp->in6p_faddr.s6_addr32[3] /* XXX */; else #endif /* INET6 */ hashkey_faddr = inp->inp_faddr.s_addr; pcbhash = &pcbinfo->hashbase[INP_PCBHASH(hashkey_faddr, inp->inp_lport, inp->inp_fport, pcbinfo->hashmask)]; pcbporthash = &pcbinfo->porthashbase[INP_PCBPORTHASH(inp->inp_lport, pcbinfo->porthashmask)]; /* * Go through port list and look for a head for this lport. */ for (phd = pcbporthash->lh_first; phd != NULL; phd = phd->phd_hash.le_next) { if (phd->phd_port == inp->inp_lport) break; } /* * If none exists, malloc one and tack it on. */ if (phd == NULL) { MALLOC(phd, struct inpcbport *, sizeof(struct inpcbport), M_PCB, M_NOWAIT); if (phd == NULL) { return (ENOBUFS); /* XXX */ } phd->phd_port = inp->inp_lport; LIST_INIT(&phd->phd_pcblist); LIST_INSERT_HEAD(pcbporthash, phd, phd_hash); } inp->inp_phd = phd; LIST_INSERT_HEAD(&phd->phd_pcblist, inp, inp_portlist); LIST_INSERT_HEAD(pcbhash, inp, inp_hash); return (0); } /* * Move PCB to the proper hash bucket when { faddr, fport } have been * changed. NOTE: This does not handle the case of the lport changing (the * hashed port list would have to be updated as well), so the lport must * not change after in_pcbinshash() has been called. */ void in_pcbrehash(inp) struct inpcb *inp; { struct inpcbhead *head; u_int32_t hashkey_faddr; #ifdef INET6 if (inp->inp_vflag & INP_IPV6) hashkey_faddr = inp->in6p_faddr.s6_addr32[3] /* XXX */; else #endif /* INET6 */ hashkey_faddr = inp->inp_faddr.s_addr; head = &inp->inp_pcbinfo->hashbase[INP_PCBHASH(hashkey_faddr, inp->inp_lport, inp->inp_fport, inp->inp_pcbinfo->hashmask)]; LIST_REMOVE(inp, inp_hash); LIST_INSERT_HEAD(head, inp, inp_hash); } /* * Remove PCB from various lists. */ void in_pcbremlists(inp) struct inpcb *inp; { inp->inp_gencnt = ++inp->inp_pcbinfo->ipi_gencnt; if (inp->inp_lport) { struct inpcbport *phd = inp->inp_phd; LIST_REMOVE(inp, inp_hash); LIST_REMOVE(inp, inp_portlist); if (phd->phd_pcblist.lh_first == NULL) { LIST_REMOVE(phd, phd_hash); free(phd, M_PCB); } } LIST_REMOVE(inp, inp_list); inp->inp_pcbinfo->ipi_count--; } int prison_xinpcb(struct proc *p, struct inpcb *inp) { if (!p->p_prison) return (0); if (ntohl(inp->inp_laddr.s_addr) == p->p_prison->pr_ip) return (0); return (1); } Index: head/sys/netinet/in_proto.c =================================================================== --- head/sys/netinet/in_proto.c (revision 62586) +++ head/sys/netinet/in_proto.c (revision 62587) @@ -1,238 +1,255 @@ /* * Copyright (c) 1982, 1986, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)in_proto.c 8.2 (Berkeley) 2/9/95 * $FreeBSD$ */ #include "opt_ipdivert.h" #include "opt_ipx.h" #include "opt_ipsec.h" #include "opt_inet6.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include #include /* * TCP/IP protocol family: IP, ICMP, UDP, TCP. */ #ifdef IPSEC #include #include #ifdef IPSEC_ESP #include #endif #endif /* IPSEC */ #include "gif.h" #if NGIF > 0 #include #endif +#include "stf.h" +#if NSTF > 0 +#include +#endif + #ifdef IPXIP #include #endif #ifdef NSIP #include #include #endif extern struct domain inetdomain; static struct pr_usrreqs nousrreqs; struct ipprotosw inetsw[] = { { 0, &inetdomain, 0, 0, 0, 0, 0, 0, 0, ip_init, 0, ip_slowtimo, ip_drain, &nousrreqs }, { SOCK_DGRAM, &inetdomain, IPPROTO_UDP, PR_ATOMIC|PR_ADDR, udp_input, 0, udp_ctlinput, ip_ctloutput, 0, udp_init, 0, 0, 0, &udp_usrreqs }, { SOCK_STREAM, &inetdomain, IPPROTO_TCP, PR_CONNREQUIRED|PR_IMPLOPCL|PR_WANTRCVD, tcp_input, 0, tcp_ctlinput, tcp_ctloutput, 0, tcp_init, 0, tcp_slowtimo, tcp_drain, &tcp_usrreqs }, { SOCK_RAW, &inetdomain, IPPROTO_RAW, PR_ATOMIC|PR_ADDR, rip_input, 0, rip_ctlinput, rip_ctloutput, 0, 0, 0, 0, 0, &rip_usrreqs }, { SOCK_RAW, &inetdomain, IPPROTO_ICMP, PR_ATOMIC|PR_ADDR, icmp_input, 0, 0, rip_ctloutput, 0, 0, 0, 0, 0, &rip_usrreqs }, { SOCK_RAW, &inetdomain, IPPROTO_IGMP, PR_ATOMIC|PR_ADDR, igmp_input, 0, 0, rip_ctloutput, 0, igmp_init, igmp_fasttimo, igmp_slowtimo, 0, &rip_usrreqs }, { SOCK_RAW, &inetdomain, IPPROTO_RSVP, PR_ATOMIC|PR_ADDR, rsvp_input, 0, 0, rip_ctloutput, 0, 0, 0, 0, 0, &rip_usrreqs }, #ifdef IPSEC { SOCK_RAW, &inetdomain, IPPROTO_AH, PR_ATOMIC|PR_ADDR, ah4_input, 0, 0, 0, 0, 0, 0, 0, 0, &nousrreqs }, #ifdef IPSEC_ESP { SOCK_RAW, &inetdomain, IPPROTO_ESP, PR_ATOMIC|PR_ADDR, esp4_input, 0, 0, 0, 0, 0, 0, 0, 0, &nousrreqs }, #endif #endif /* IPSEC */ -#if NGIF > 0 { SOCK_RAW, &inetdomain, IPPROTO_IPV4, PR_ATOMIC|PR_ADDR, - in_gif_input, 0, 0, 0, + encap4_input, 0, 0, rip_ctloutput, 0, - 0, 0, 0, 0, + encap_init, 0, 0, 0, &nousrreqs }, # ifdef INET6 { SOCK_RAW, &inetdomain, IPPROTO_IPV6, PR_ATOMIC|PR_ADDR, - in_gif_input, 0, 0, 0, + encap4_input, 0, 0, rip_ctloutput, 0, 0, 0, 0, 0, &nousrreqs }, #endif -#else /*NGIF*/ -{ SOCK_RAW, &inetdomain, IPPROTO_IPIP, PR_ATOMIC|PR_ADDR, - ipip_input, 0, 0, rip_ctloutput, - 0, - 0, 0, 0, 0, - &rip_usrreqs -}, -#endif /*NGIF*/ #ifdef IPDIVERT { SOCK_RAW, &inetdomain, IPPROTO_DIVERT, PR_ATOMIC|PR_ADDR, div_input, 0, 0, ip_ctloutput, 0, div_init, 0, 0, 0, &div_usrreqs, }, #endif #ifdef IPXIP { SOCK_RAW, &inetdomain, IPPROTO_IDP, PR_ATOMIC|PR_ADDR, ipxip_input, 0, ipxip_ctlinput, 0, 0, 0, 0, 0, 0, &rip_usrreqs }, #endif #ifdef NSIP { SOCK_RAW, &inetdomain, IPPROTO_IDP, PR_ATOMIC|PR_ADDR, idpip_input, 0, nsip_ctlinput, 0, 0, 0, 0, 0, 0, &rip_usrreqs }, #endif /* raw wildcard */ { SOCK_RAW, &inetdomain, 0, PR_ATOMIC|PR_ADDR, rip_input, 0, 0, rip_ctloutput, 0, rip_init, 0, 0, 0, &rip_usrreqs }, }; + +#if NGIF > 0 +struct ipprotosw in_gif_protosw = +{ SOCK_RAW, &inetdomain, 0/*IPPROTO_IPV[46]*/, PR_ATOMIC|PR_ADDR, + in_gif_input, rip_output, 0, rip_ctloutput, + 0, + 0, 0, 0, 0, + &rip_usrreqs +}; +#endif /*NGIF*/ + +#if NSTF > 0 +struct ipprotosw in_stf_protosw = +{ SOCK_RAW, &inetdomain, IPPROTO_IPV6, PR_ATOMIC|PR_ADDR, + in_stf_input, rip_output, 0, rip_ctloutput, + 0, + 0, 0, 0, 0, + &rip_usrreqs +}; +#endif /*NSTF*/ extern int in_inithead __P((void **, int)); struct domain inetdomain = { AF_INET, "internet", 0, 0, 0, (struct protosw *)inetsw, (struct protosw *)&inetsw[sizeof(inetsw)/sizeof(inetsw[0])], 0, in_inithead, 32, sizeof(struct sockaddr_in) }; DOMAIN_SET(inet); SYSCTL_NODE(_net, PF_INET, inet, CTLFLAG_RW, 0, "Internet Family"); SYSCTL_NODE(_net_inet, IPPROTO_IP, ip, CTLFLAG_RW, 0, "IP"); SYSCTL_NODE(_net_inet, IPPROTO_ICMP, icmp, CTLFLAG_RW, 0, "ICMP"); SYSCTL_NODE(_net_inet, IPPROTO_UDP, udp, CTLFLAG_RW, 0, "UDP"); SYSCTL_NODE(_net_inet, IPPROTO_TCP, tcp, CTLFLAG_RW, 0, "TCP"); SYSCTL_NODE(_net_inet, IPPROTO_IGMP, igmp, CTLFLAG_RW, 0, "IGMP"); #ifdef IPSEC SYSCTL_NODE(_net_inet, IPPROTO_AH, ipsec, CTLFLAG_RW, 0, "IPSEC"); #endif /* IPSEC */ SYSCTL_NODE(_net_inet, IPPROTO_RAW, raw, CTLFLAG_RW, 0, "RAW"); #ifdef IPDIVERT SYSCTL_NODE(_net_inet, IPPROTO_DIVERT, div, CTLFLAG_RW, 0, "DIVERT"); #endif Index: head/sys/netinet/ip6.h =================================================================== --- head/sys/netinet/ip6.h (revision 62586) +++ head/sys/netinet/ip6.h (revision 62587) @@ -1,34 +1,304 @@ +/* $FreeBSD$ */ +/* $KAME: ip6.h,v 1.9 2000/07/02 21:01:32 itojun Exp $ */ + /* - * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project. + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. + */ + +/* + * Copyright (c) 1982, 1986, 1993 + * The Regents of the University of California. All rights reserved. * - * $FreeBSD$ + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ip.h 8.1 (Berkeley) 6/10/93 */ -#define __KAME_NETINET_IP6_H_INCLUDED_ -#include -#undef __KAME_NETINET_IP6_H_INCLUDED_ +#ifndef _NETINET_IP6_H_ +#define _NETINET_IP6_H_ + +/* + * Definition for internet protocol version 6. + * RFC 2460 + */ + +struct ip6_hdr { + union { + struct ip6_hdrctl { + u_int32_t ip6_un1_flow; /* 20 bits of flow-ID */ + u_int16_t ip6_un1_plen; /* payload length */ + u_int8_t ip6_un1_nxt; /* next header */ + u_int8_t ip6_un1_hlim; /* hop limit */ + } ip6_un1; + u_int8_t ip6_un2_vfc; /* 4 bits version, top 4 bits class */ + } ip6_ctlun; + struct in6_addr ip6_src; /* source address */ + struct in6_addr ip6_dst; /* destination address */ +}; + +#define ip6_vfc ip6_ctlun.ip6_un2_vfc +#define ip6_flow ip6_ctlun.ip6_un1.ip6_un1_flow +#define ip6_plen ip6_ctlun.ip6_un1.ip6_un1_plen +#define ip6_nxt ip6_ctlun.ip6_un1.ip6_un1_nxt +#define ip6_hlim ip6_ctlun.ip6_un1.ip6_un1_hlim +#define ip6_hops ip6_ctlun.ip6_un1.ip6_un1_hlim + +#define IPV6_VERSION 0x60 +#define IPV6_VERSION_MASK 0xf0 + +#if BYTE_ORDER == BIG_ENDIAN +#define IPV6_FLOWINFO_MASK 0x0fffffff /* flow info (28 bits) */ +#define IPV6_FLOWLABEL_MASK 0x000fffff /* flow label (20 bits) */ +#else +#if BYTE_ORDER == LITTLE_ENDIAN +#define IPV6_FLOWINFO_MASK 0xffffff0f /* flow info (28 bits) */ +#define IPV6_FLOWLABEL_MASK 0xffff0f00 /* flow label (20 bits) */ +#endif /* LITTLE_ENDIAN */ +#endif +/* ECN bits proposed by Sally Floyd */ +#define IP6TOS_CE 0x01 /* congestion experienced */ +#define IP6TOS_ECT 0x02 /* ECN-capable transport */ + +/* + * Extension Headers + */ + +struct ip6_ext { + u_char ip6e_nxt; + u_char ip6e_len; +}; + +/* Hop-by-Hop options header */ +/* XXX should we pad it to force alignment on an 8-byte boundary? */ +struct ip6_hbh { + u_int8_t ip6h_nxt; /* next header */ + u_int8_t ip6h_len; /* length in units of 8 octets */ + /* followed by options */ +}; + +/* Destination options header */ +/* XXX should we pad it to force alignment on an 8-byte boundary? */ +struct ip6_dest { + u_int8_t ip6d_nxt; /* next header */ + u_int8_t ip6d_len; /* length in units of 8 octets */ + /* followed by options */ +}; + +/* Option types and related macros */ +#define IP6OPT_PAD1 0x00 /* 00 0 00000 */ +#define IP6OPT_PADN 0x01 /* 00 0 00001 */ +#define IP6OPT_JUMBO 0xC2 /* 11 0 00010 = 194 */ +#define IP6OPT_JUMBO_LEN 6 +#define IP6OPT_RTALERT 0x05 /* 00 0 00101 */ +#define IP6OPT_RTALERT_LEN 4 +#define IP6OPT_RTALERT_MLD 0 /* Datagram contains an MLD message */ +#define IP6OPT_RTALERT_RSVP 1 /* Datagram contains an RSVP message */ +#define IP6OPT_RTALERT_ACTNET 2 /* contains an Active Networks msg */ +#define IP6OPT_MINLEN 2 + +#define IP6OPT_TYPE(o) ((o) & 0xC0) +#define IP6OPT_TYPE_SKIP 0x00 +#define IP6OPT_TYPE_DISCARD 0x40 +#define IP6OPT_TYPE_FORCEICMP 0x80 +#define IP6OPT_TYPE_ICMP 0xC0 + +#define IP6OPT_MUTABLE 0x20 + +/* Routing header */ +struct ip6_rthdr { + u_int8_t ip6r_nxt; /* next header */ + u_int8_t ip6r_len; /* length in units of 8 octets */ + u_int8_t ip6r_type; /* routing type */ + u_int8_t ip6r_segleft; /* segments left */ + /* followed by routing type specific data */ +}; + +/* Type 0 Routing header */ +struct ip6_rthdr0 { + u_int8_t ip6r0_nxt; /* next header */ + u_int8_t ip6r0_len; /* length in units of 8 octets */ + u_int8_t ip6r0_type; /* always zero */ + u_int8_t ip6r0_segleft; /* segments left */ + u_int8_t ip6r0_reserved; /* reserved field */ + u_int8_t ip6r0_slmap[3]; /* strict/loose bit map */ + struct in6_addr ip6r0_addr[1]; /* up to 23 addresses */ +}; + +/* Fragment header */ +struct ip6_frag { + u_int8_t ip6f_nxt; /* next header */ + u_int8_t ip6f_reserved; /* reserved field */ + u_int16_t ip6f_offlg; /* offset, reserved, and flag */ + u_int32_t ip6f_ident; /* identification */ +}; + +#if BYTE_ORDER == BIG_ENDIAN +#define IP6F_OFF_MASK 0xfff8 /* mask out offset from _offlg */ +#define IP6F_RESERVED_MASK 0x0006 /* reserved bits in ip6f_offlg */ +#define IP6F_MORE_FRAG 0x0001 /* more-fragments flag */ +#else /* BYTE_ORDER == LITTLE_ENDIAN */ +#define IP6F_OFF_MASK 0xf8ff /* mask out offset from _offlg */ +#define IP6F_RESERVED_MASK 0x0600 /* reserved bits in ip6f_offlg */ +#define IP6F_MORE_FRAG 0x0100 /* more-fragments flag */ +#endif /* BYTE_ORDER == LITTLE_ENDIAN */ + +/* + * Internet implementation parameters. + */ +#define IPV6_MAXHLIM 255 /* maximun hoplimit */ +#define IPV6_DEFHLIM 64 /* default hlim */ +#define IPV6_FRAGTTL 120 /* ttl for fragment packets, in slowtimo tick */ +#define IPV6_HLIMDEC 1 /* subtracted when forwaeding */ + +#define IPV6_MMTU 1280 /* minimal MTU and reassembly. 1024 + 256 */ +#define IPV6_MAXPACKET 65535 /* ip6 max packet size without Jumbo payload*/ + +#ifdef _KERNEL +/* + * IP6_EXTHDR_CHECK ensures that region between the IP6 header and the + * target header (including IPv6 itself, extension headers and + * TCP/UDP/ICMP6 headers) are continuous. KAME requires drivers + * to store incoming data into one internal mbuf or one or more external + * mbufs(never into two or more internal mbufs). Thus, the third case is + * supposed to never be matched but is prepared just in case. + */ + +#ifdef INET6 +#define IP6_EXTHDR_STAT(x) x +#else +#define IP6_EXTHDR_STAT(x) +#endif + +#define IP6_EXTHDR_CHECK(m, off, hlen, ret) \ +do { \ + if ((m)->m_next != NULL) { \ + if (((m)->m_flags & M_LOOP) && \ + ((m)->m_len < (off) + (hlen)) && \ + (((m) = m_pullup((m), (off) + (hlen))) == NULL)) { \ + IP6_EXTHDR_STAT(ip6stat.ip6s_exthdrtoolong++); \ + return ret; \ + } else if ((m)->m_flags & M_EXT) { \ + if ((m)->m_len < (off) + (hlen)) { \ + IP6_EXTHDR_STAT(ip6stat.ip6s_exthdrtoolong++); \ + m_freem(m); \ + return ret; \ + } \ + } else { \ + if ((m)->m_len < (off) + (hlen)) { \ + IP6_EXTHDR_STAT(ip6stat.ip6s_exthdrtoolong++); \ + m_freem(m); \ + return ret; \ + } \ + } \ + } else { \ + if ((m)->m_len < (off) + (hlen)) { \ + IP6_EXTHDR_STAT(ip6stat.ip6s_tooshort++); \ + in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_truncated); \ + m_freem(m); \ + return ret; \ + } \ + } \ +} while (0) + +/* + * IP6_EXTHDR_GET ensures that intermediate protocol header (from "off" to + * "len") is located in single mbuf, on contiguous memory region. + * The pointer to the region will be returned to pointer variable "val", + * with type "typ". + * IP6_EXTHDR_GET0 does the same, except that it aligns the structure at the + * very top of mbuf. GET0 is likely to make memory copy than GET. + * + * XXX we're now testing this, needs m_pulldown() + */ +#define IP6_EXTHDR_GET(val, typ, m, off, len) \ +do { \ + struct mbuf *t; \ + int tmp; \ + IP6_EXTHDR_STAT(ip6stat.ip6s_exthdrget++); \ + if ((m)->m_len >= (off) + (len)) \ + (val) = (typ)(mtod((m), caddr_t) + (off)); \ + else { \ + t = m_pulldown((m), (off), (len), &tmp); \ + if (t) { \ + if (t->m_len < tmp + (len)) \ + panic("m_pulldown malfunction"); \ + (val) = (typ)(mtod(t, caddr_t) + tmp); \ + } else { \ + (val) = (typ)NULL; \ + (m) = NULL; \ + } \ + } \ +} while (0) + +#define IP6_EXTHDR_GET0(val, typ, m, off, len) \ +do { \ + struct mbuf *t; \ + IP6_EXTHDR_STAT(ip6stat.ip6s_exthdrget0++); \ + if ((off) == 0) \ + (val) = (typ)mtod(m, caddr_t); \ + else { \ + t = m_pulldown((m), (off), (len), NULL); \ + if (t) { \ + if (t->m_len < (len)) \ + panic("m_pulldown malfunction"); \ + (val) = (typ)mtod(t, caddr_t); \ + } else { \ + (val) = (typ)NULL; \ + (m) = NULL; \ + } \ + } \ +} while (0) +#endif /*_KERNEL*/ + +#endif /* not _NETINET_IP6_H_ */ Index: head/sys/netinet/ip_ecn.c =================================================================== --- head/sys/netinet/ip_ecn.c (revision 62586) +++ head/sys/netinet/ip_ecn.c (revision 62587) @@ -1,148 +1,149 @@ +/* $FreeBSD$ */ +/* $KAME: ip_ecn.c,v 1.7 2000/05/05 11:00:56 sumikawa Exp $ */ + /* * Copyright (C) 1999 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: ip_ecn.c,v 1.2 1999/07/30 12:17:15 itojun Exp $ - * $FreeBSD$ */ /* * ECN consideration on tunnel ingress/egress operation. * http://www.aciri.org/floyd/papers/draft-ipsec-ecn-00.txt */ #include "opt_inet.h" #include "opt_inet6.h" #include #include #include #include #ifdef INET #include #include #include #endif #ifdef INET6 #ifndef INET #include #endif #include #endif #include #ifdef INET6 #include #endif /* * modify outer ECN (TOS) field on ingress operation (tunnel encapsulation). * call it after you've done the default initialization/copy for the outer. */ void ip_ecn_ingress(mode, outer, inner) int mode; u_int8_t *outer; u_int8_t *inner; { if (!outer || !inner) panic("NULL pointer passed to ip_ecn_ingress"); switch (mode) { case ECN_ALLOWED: /* ECN allowed */ *outer &= ~IPTOS_CE; break; case ECN_FORBIDDEN: /* ECN forbidden */ *outer &= ~(IPTOS_ECT | IPTOS_CE); break; case ECN_NOCARE: /* no consideration to ECN */ break; } } /* * modify inner ECN (TOS) field on egress operation (tunnel decapsulation). * call it after you've done the default initialization/copy for the inner. */ void ip_ecn_egress(mode, outer, inner) int mode; u_int8_t *outer; u_int8_t *inner; { if (!outer || !inner) panic("NULL pointer passed to ip_ecn_egress"); switch (mode) { case ECN_ALLOWED: if (*outer & IPTOS_CE) *inner |= IPTOS_CE; break; case ECN_FORBIDDEN: /* ECN forbidden */ case ECN_NOCARE: /* no consideration to ECN */ break; } } #ifdef INET6 void ip6_ecn_ingress(mode, outer, inner) int mode; u_int32_t *outer; u_int32_t *inner; { u_int8_t outer8, inner8; if (!outer || !inner) panic("NULL pointer passed to ip6_ecn_ingress"); outer8 = (ntohl(*outer) >> 20) & 0xff; inner8 = (ntohl(*inner) >> 20) & 0xff; ip_ecn_ingress(mode, &outer8, &inner8); *outer &= ~htonl(0xff << 20); *outer |= htonl((u_int32_t)outer8 << 20); } void ip6_ecn_egress(mode, outer, inner) int mode; u_int32_t *outer; u_int32_t *inner; { u_int8_t outer8, inner8; if (!outer || !inner) panic("NULL pointer passed to ip6_ecn_egress"); outer8 = (ntohl(*outer) >> 20) & 0xff; inner8 = (ntohl(*inner) >> 20) & 0xff; ip_ecn_egress(mode, &outer8, &inner8); *inner &= ~htonl(0xff << 20); *inner |= htonl((u_int32_t)inner8 << 20); } #endif Index: head/sys/netinet/ip_ecn.h =================================================================== --- head/sys/netinet/ip_ecn.h (revision 62586) +++ head/sys/netinet/ip_ecn.h (revision 62587) @@ -1,44 +1,45 @@ +/* $FreeBSD$ */ +/* $KAME: ip_ecn.h,v 1.5 2000/03/27 04:58:38 sumikawa Exp $ */ + /* * Copyright (C) 1999 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: ip_ecn.h,v 1.2 1999/08/19 12:57:44 itojun Exp $ - * $FreeBSD$ */ /* * ECN consideration on tunnel ingress/egress operation. * http://www.aciri.org/floyd/papers/draft-ipsec-ecn-00.txt */ -#define ECN_ALLOWED 1 /* ECN allowed */ -#define ECN_FORBIDDEN 0 /* ECN forbidden */ -#define ECN_NOCARE (-1) /* no consideration to ECN */ +#define ECN_ALLOWED 1 /* ECN allowed */ +#define ECN_FORBIDDEN 0 /* ECN forbidden */ +#define ECN_NOCARE (-1) /* no consideration to ECN */ #ifdef _KERNEL extern void ip_ecn_ingress __P((int, u_int8_t *, u_int8_t *)); extern void ip_ecn_egress __P((int, u_int8_t *, u_int8_t *)); #endif Index: head/sys/netinet/ip_encap.c =================================================================== --- head/sys/netinet/ip_encap.c (nonexistent) +++ head/sys/netinet/ip_encap.c (revision 62587) @@ -0,0 +1,534 @@ +/* $FreeBSD$ */ +/* $KAME: ip_encap.c,v 1.36 2000/06/17 20:34:24 itojun Exp $ */ + +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * My grandfather said that there's a devil inside tunnelling technology... + * + * We have surprisingly many protocols that want packets with IP protocol + * #4 or #41. Here's a list of protocols that want protocol #41: + * RFC1933 configured tunnel + * RFC1933 automatic tunnel + * RFC2401 IPsec tunnel + * RFC2473 IPv6 generic packet tunnelling + * RFC2529 6over4 tunnel + * mobile-ip6 (uses RFC2473) + * 6to4 tunnel + * Here's a list of protocol that want protocol #4: + * RFC1853 IPv4-in-IPv4 tunnelling + * RFC2003 IPv4 encapsulation within IPv4 + * RFC2344 reverse tunnelling for mobile-ip4 + * RFC2401 IPsec tunnel + * Well, what can I say. They impose different en/decapsulation mechanism + * from each other, so they need separate protocol handler. The only one + * we can easily determine by protocol # is IPsec, which always has + * AH/ESP/IPComp header right after outer IP header. + * + * So, clearly good old protosw does not work for protocol #4 and #41. + * The code will let you match protocol via src/dst address pair. + */ +/* XXX is M_NETADDR correct? */ + +#include "opt_mrouting.h" +#include "opt_inet.h" +#include "opt_inet6.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#ifdef MROUTING +#include +#endif /* MROUTING */ +#include + +#ifdef INET6 +#include +#include +#include +#endif + +#include + +#include + +#include +#include +MALLOC_DEFINE(M_NETADDR, "Export Host", "Export host address structure"); + +static void encap_add __P((struct encaptab *)); +static int mask_match __P((const struct encaptab *, const struct sockaddr *, + const struct sockaddr *)); +static void encap_fillarg __P((struct mbuf *, const struct encaptab *)); + +/* rely upon BSS initialization */ +LIST_HEAD(, encaptab) encaptab; + +void +encap_init() +{ +#if 0 + /* + * we cannot use LIST_INIT() here, since drivers may want to call + * encap_attach(), on driver attach. encap_init() will be called + * on AF_INET{,6} initialization, which happens after driver + * initialization - using LIST_INIT() here can nuke encap_attach() + * from drivers. + */ + LIST_INIT(&encaptab); +#endif +} + +void +#if __STDC__ +encap4_input(struct mbuf *m, ...) +#else +encap4_input(m, va_alist) + struct mbuf *m; + va_dcl +#endif +{ + int off, proto; + struct ip *ip; + struct sockaddr_in s, d; + const struct ipprotosw *psw; + struct encaptab *ep, *match; + va_list ap; + int prio, matchprio; + + va_start(ap, m); + off = va_arg(ap, int); + proto = va_arg(ap, int); + va_end(ap); + + ip = mtod(m, struct ip *); + + bzero(&s, sizeof(s)); + s.sin_family = AF_INET; + s.sin_len = sizeof(struct sockaddr_in); + s.sin_addr = ip->ip_src; + bzero(&d, sizeof(d)); + d.sin_family = AF_INET; + d.sin_len = sizeof(struct sockaddr_in); + d.sin_addr = ip->ip_dst; + + match = NULL; + matchprio = 0; + for (ep = LIST_FIRST(&encaptab); ep; ep = LIST_NEXT(ep, chain)) { + if (ep->af != AF_INET) + continue; + if (ep->proto >= 0 && ep->proto != proto) + continue; + if (ep->func) + prio = (*ep->func)(m, off, proto, ep->arg); + else { + /* + * it's inbound traffic, we need to match in reverse + * order + */ + prio = mask_match(ep, (struct sockaddr *)&d, + (struct sockaddr *)&s); + } + + /* + * We prioritize the matches by using bit length of the + * matches. mask_match() and user-supplied matching function + * should return the bit length of the matches (for example, + * if both src/dst are matched for IPv4, 64 should be returned). + * 0 or negative return value means "it did not match". + * + * The question is, since we have two "mask" portion, we + * cannot really define total order between entries. + * For example, which of these should be preferred? + * mask_match() returns 48 (32 + 16) for both of them. + * src=3ffe::/16, dst=3ffe:501::/32 + * src=3ffe:501::/32, dst=3ffe::/16 + * + * We need to loop through all the possible candidates + * to get the best match - the search takes O(n) for + * n attachments (i.e. interfaces). + */ + if (prio <= 0) + continue; + if (prio > matchprio) { + matchprio = prio; + match = ep; + } + } + + if (match) { + /* found a match, "match" has the best one */ + psw = (const struct ipprotosw *)match->psw; + if (psw && psw->pr_input) { + encap_fillarg(m, match); + (*psw->pr_input)(m, off, proto); + } else + m_freem(m); + return; + } + + /* for backward compatibility */ +# ifdef MROUTING +# define COMPATFUNC ipip_input +# endif /*MROUTING*/ + +#ifdef COMPATFUNC + if (proto == IPPROTO_IPV4) { + COMPATFUNC(m, off, proto); + return; + } +#endif + + /* last resort: inject to raw socket */ + rip_input(m, off, proto); +} + +#ifdef INET6 +int +encap6_input(mp, offp, proto) + struct mbuf **mp; + int *offp; + int proto; +{ + struct mbuf *m = *mp; + struct ip6_hdr *ip6; + struct sockaddr_in6 s, d; + const struct ip6protosw *psw; + struct encaptab *ep, *match; + int prio, matchprio; + + ip6 = mtod(m, struct ip6_hdr *); + + bzero(&s, sizeof(s)); + s.sin6_family = AF_INET6; + s.sin6_len = sizeof(struct sockaddr_in6); + s.sin6_addr = ip6->ip6_src; + bzero(&d, sizeof(d)); + d.sin6_family = AF_INET6; + d.sin6_len = sizeof(struct sockaddr_in6); + d.sin6_addr = ip6->ip6_dst; + + match = NULL; + matchprio = 0; + for (ep = LIST_FIRST(&encaptab); ep; ep = LIST_NEXT(ep, chain)) { + if (ep->af != AF_INET6) + continue; + if (ep->proto >= 0 && ep->proto != proto) + continue; + if (ep->func) + prio = (*ep->func)(m, *offp, proto, ep->arg); + else { + /* + * it's inbound traffic, we need to match in reverse + * order + */ + prio = mask_match(ep, (struct sockaddr *)&d, + (struct sockaddr *)&s); + } + + /* see encap4_input() for issues here */ + if (prio <= 0) + continue; + if (prio > matchprio) { + matchprio = prio; + match = ep; + } + } + + if (match) { + /* found a match */ + psw = (const struct ip6protosw *)match->psw; + if (psw && psw->pr_input) { + encap_fillarg(m, match); + return (*psw->pr_input)(mp, offp, proto); + } else { + m_freem(m); + return IPPROTO_DONE; + } + } + + /* last resort: inject to raw socket */ + return rip6_input(mp, offp, proto); +} +#endif + +static void +encap_add(ep) + struct encaptab *ep; +{ + + LIST_INSERT_HEAD(&encaptab, ep, chain); +} + +/* + * sp (src ptr) is always my side, and dp (dst ptr) is always remote side. + * length of mask (sm and dm) is assumed to be same as sp/dp. + * Return value will be necessary as input (cookie) for encap_detach(). + */ +const struct encaptab * +encap_attach(af, proto, sp, sm, dp, dm, psw, arg) + int af; + int proto; + const struct sockaddr *sp, *sm; + const struct sockaddr *dp, *dm; + const struct protosw *psw; + void *arg; +{ + struct encaptab *ep; + int error; + int s; + + s = splnet(); + /* sanity check on args */ + if (sp->sa_len > sizeof(ep->src) || dp->sa_len > sizeof(ep->dst)) { + error = EINVAL; + goto fail; + } + if (sp->sa_len != dp->sa_len) { + error = EINVAL; + goto fail; + } + if (af != sp->sa_family || af != dp->sa_family) { + error = EINVAL; + goto fail; + } + + /* check if anyone have already attached with exactly same config */ + for (ep = LIST_FIRST(&encaptab); ep; ep = LIST_NEXT(ep, chain)) { + if (ep->af != af) + continue; + if (ep->proto != proto) + continue; + if (ep->src.ss_len != sp->sa_len || + bcmp(&ep->src, sp, sp->sa_len) != 0 || + bcmp(&ep->srcmask, sm, sp->sa_len) != 0) + continue; + if (ep->dst.ss_len != dp->sa_len || + bcmp(&ep->dst, dp, dp->sa_len) != 0 || + bcmp(&ep->dstmask, dm, dp->sa_len) != 0) + continue; + + error = EEXIST; + goto fail; + } + + ep = malloc(sizeof(*ep), M_NETADDR, M_NOWAIT); /*XXX*/ + if (ep == NULL) { + error = ENOBUFS; + goto fail; + } + bzero(ep, sizeof(*ep)); + + ep->af = af; + ep->proto = proto; + bcopy(sp, &ep->src, sp->sa_len); + bcopy(sm, &ep->srcmask, sp->sa_len); + bcopy(dp, &ep->dst, dp->sa_len); + bcopy(dm, &ep->dstmask, dp->sa_len); + ep->psw = psw; + ep->arg = arg; + + encap_add(ep); + + error = 0; + splx(s); + return ep; + +fail: + splx(s); + return NULL; +} + +const struct encaptab * +encap_attach_func(af, proto, func, psw, arg) + int af; + int proto; + int (*func) __P((const struct mbuf *, int, int, void *)); + const struct protosw *psw; + void *arg; +{ + struct encaptab *ep; + int error; + int s; + + s = splnet(); + /* sanity check on args */ + if (!func) { + error = EINVAL; + goto fail; + } + + ep = malloc(sizeof(*ep), M_NETADDR, M_NOWAIT); /*XXX*/ + if (ep == NULL) { + error = ENOBUFS; + goto fail; + } + bzero(ep, sizeof(*ep)); + + ep->af = af; + ep->proto = proto; + ep->func = func; + ep->psw = psw; + ep->arg = arg; + + encap_add(ep); + + error = 0; + splx(s); + return ep; + +fail: + splx(s); + return NULL; +} + +int +encap_detach(cookie) + const struct encaptab *cookie; +{ + const struct encaptab *ep = cookie; + struct encaptab *p; + + for (p = LIST_FIRST(&encaptab); p; p = LIST_NEXT(p, chain)) { + if (p == ep) { + LIST_REMOVE(p, chain); + free(p, M_NETADDR); /*XXX*/ + return 0; + } + } + + return EINVAL; +} + +static int +mask_match(ep, sp, dp) + const struct encaptab *ep; + const struct sockaddr *sp; + const struct sockaddr *dp; +{ + struct sockaddr_storage s; + struct sockaddr_storage d; + int i; + const u_int8_t *p, *q; + u_int8_t *r; + int matchlen; + + if (sp->sa_len > sizeof(s) || dp->sa_len > sizeof(d)) + return 0; + if (sp->sa_family != ep->af || dp->sa_family != ep->af) + return 0; + if (sp->sa_len != ep->src.ss_len || dp->sa_len != ep->dst.ss_len) + return 0; + + matchlen = 0; + + p = (const u_int8_t *)sp; + q = (const u_int8_t *)&ep->srcmask; + r = (u_int8_t *)&s; + for (i = 0 ; i < sp->sa_len; i++) { + r[i] = p[i] & q[i]; + /* XXX estimate */ + matchlen += (q[i] ? 8 : 0); + } + + p = (const u_int8_t *)dp; + q = (const u_int8_t *)&ep->dstmask; + r = (u_int8_t *)&d; + for (i = 0 ; i < dp->sa_len; i++) { + r[i] = p[i] & q[i]; + /* XXX rough estimate */ + matchlen += (q[i] ? 8 : 0); + } + + /* need to overwrite len/family portion as we don't compare them */ + s.ss_len = sp->sa_len; + s.ss_family = sp->sa_family; + d.ss_len = dp->sa_len; + d.ss_family = dp->sa_family; + + if (bcmp(&s, &ep->src, ep->src.ss_len) == 0 && + bcmp(&d, &ep->dst, ep->dst.ss_len) == 0) { + return matchlen; + } else + return 0; +} + +static void +encap_fillarg(m, ep) + struct mbuf *m; + const struct encaptab *ep; +{ +#if 0 + m->m_pkthdr.aux = ep->arg; +#else + struct mbuf *n; + + n = m_aux_add(m, AF_INET, IPPROTO_IPV4); + if (n) { + *mtod(n, void **) = ep->arg; + n->m_len = sizeof(void *); + } +#endif +} + +void * +encap_getarg(m) + struct mbuf *m; +{ + void *p; +#if 0 + p = m->m_pkthdr.aux; + m->m_pkthdr.aux = NULL; + return p; +#else + struct mbuf *n; + + p = NULL; + n = m_aux_find(m, AF_INET, IPPROTO_IPV4); + if (n) { + if (n->m_len == sizeof(void *)) + p = *mtod(n, void **); + m_aux_delete(m, n); + } + return p; +#endif +} Property changes on: head/sys/netinet/ip_encap.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: head/sys/netinet/ip_encap.h =================================================================== --- head/sys/netinet/ip_encap.h (nonexistent) +++ head/sys/netinet/ip_encap.h (revision 62587) @@ -0,0 +1,64 @@ +/* $FreeBSD$ */ +/* $KAME: ip_encap.h,v 1.7 2000/03/25 07:23:37 sumikawa Exp $ */ + +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _NETINET_IP_ENCAP_H_ +#define _NETINET_IP_ENCAP_H_ + +#ifdef _KERNEL + +struct encaptab { + LIST_ENTRY(encaptab) chain; + int af; + int proto; /* -1: don't care, I'll check myself */ + struct sockaddr_storage src; /* my addr */ + struct sockaddr_storage srcmask; + struct sockaddr_storage dst; /* remote addr */ + struct sockaddr_storage dstmask; + int (*func) __P((const struct mbuf *, int, int, void *)); + const struct protosw *psw; /* only pr_input will be used */ + void *arg; /* passed via m->m_pkthdr.aux */ +}; + +void encap_init __P((void)); +void encap4_input __P((struct mbuf *, ...)); +int encap6_input __P((struct mbuf **, int *, int)); +const struct encaptab *encap_attach __P((int, int, const struct sockaddr *, + const struct sockaddr *, const struct sockaddr *, + const struct sockaddr *, const struct protosw *, void *)); +const struct encaptab *encap_attach_func __P((int, int, + int (*) __P((const struct mbuf *, int, int, void *)), + const struct protosw *, void *)); +int encap_detach __P((const struct encaptab *)); +void *encap_getarg __P((struct mbuf *)); +#endif + +#endif /*_NETINET_IP_ENCAP_H_*/ Property changes on: head/sys/netinet/ip_encap.h ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: head/sys/netinet/ip_input.c =================================================================== --- head/sys/netinet/ip_input.c (revision 62586) +++ head/sys/netinet/ip_input.c (revision 62587) @@ -1,1779 +1,1774 @@ /* * Copyright (c) 1982, 1986, 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)ip_input.c 8.2 (Berkeley) 1/4/94 * $FreeBSD$ */ #define _IP_VHL #include "opt_bootp.h" #include "opt_ipfw.h" #include "opt_ipdn.h" #include "opt_ipdivert.h" #include "opt_ipfilter.h" #include "opt_ipstealth.h" #include "opt_ipsec.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef IPSEC #include #include -#ifdef IPSEC_DEBUG -#include -#else -#define KEYDEBUG(lev,arg) -#endif #endif #include "faith.h" #if defined(NFAITH) && NFAITH > 0 #include #endif #ifdef DUMMYNET #include #endif int rsvp_on = 0; static int ip_rsvp_on; struct socket *ip_rsvpd; int ipforwarding = 0; SYSCTL_INT(_net_inet_ip, IPCTL_FORWARDING, forwarding, CTLFLAG_RW, &ipforwarding, 0, "Enable IP forwarding between interfaces"); static int ipsendredirects = 1; /* XXX */ SYSCTL_INT(_net_inet_ip, IPCTL_SENDREDIRECTS, redirect, CTLFLAG_RW, &ipsendredirects, 0, "Enable sending IP redirects"); int ip_defttl = IPDEFTTL; SYSCTL_INT(_net_inet_ip, IPCTL_DEFTTL, ttl, CTLFLAG_RW, &ip_defttl, 0, "Maximum TTL on IP packets"); static int ip_dosourceroute = 0; SYSCTL_INT(_net_inet_ip, IPCTL_SOURCEROUTE, sourceroute, CTLFLAG_RW, &ip_dosourceroute, 0, "Enable forwarding source routed IP packets"); static int ip_acceptsourceroute = 0; SYSCTL_INT(_net_inet_ip, IPCTL_ACCEPTSOURCEROUTE, accept_sourceroute, CTLFLAG_RW, &ip_acceptsourceroute, 0, "Enable accepting source routed IP packets"); static int ip_keepfaith = 0; SYSCTL_INT(_net_inet_ip, IPCTL_KEEPFAITH, keepfaith, CTLFLAG_RW, &ip_keepfaith, 0, "Enable packet capture for FAITH IPv4->IPv6 translater daemon"); #ifdef DIAGNOSTIC static int ipprintfs = 0; #endif extern struct domain inetdomain; extern struct ipprotosw inetsw[]; u_char ip_protox[IPPROTO_MAX]; static int ipqmaxlen = IFQ_MAXLEN; struct in_ifaddrhead in_ifaddrhead; /* first inet address */ SYSCTL_INT(_net_inet_ip, IPCTL_INTRQMAXLEN, intr_queue_maxlen, CTLFLAG_RW, &ipintrq.ifq_maxlen, 0, "Maximum size of the IP input queue"); SYSCTL_INT(_net_inet_ip, IPCTL_INTRQDROPS, intr_queue_drops, CTLFLAG_RD, &ipintrq.ifq_drops, 0, "Number of packets dropped from the IP input queue"); struct ipstat ipstat; SYSCTL_STRUCT(_net_inet_ip, IPCTL_STATS, stats, CTLFLAG_RD, &ipstat, ipstat, "IP statistics (struct ipstat, netinet/ip_var.h)"); /* Packet reassembly stuff */ #define IPREASS_NHASH_LOG2 6 #define IPREASS_NHASH (1 << IPREASS_NHASH_LOG2) #define IPREASS_HMASK (IPREASS_NHASH - 1) #define IPREASS_HASH(x,y) \ (((((x) & 0xF) | ((((x) >> 8) & 0xF) << 4)) ^ (y)) & IPREASS_HMASK) static struct ipq ipq[IPREASS_NHASH]; static int nipq = 0; /* total # of reass queues */ static int maxnipq; const int ipintrq_present = 1; #ifdef IPCTL_DEFMTU SYSCTL_INT(_net_inet_ip, IPCTL_DEFMTU, mtu, CTLFLAG_RW, &ip_mtu, 0, "Default MTU"); #endif #ifdef IPSTEALTH static int ipstealth = 0; SYSCTL_INT(_net_inet_ip, OID_AUTO, stealth, CTLFLAG_RW, &ipstealth, 0, ""); #endif /* Firewall hooks */ ip_fw_chk_t *ip_fw_chk_ptr; ip_fw_ctl_t *ip_fw_ctl_ptr; int fw_enable = 1 ; #ifdef DUMMYNET ip_dn_ctl_t *ip_dn_ctl_ptr; #endif int (*fr_checkp) __P((struct ip *, int, struct ifnet *, int, struct mbuf **)) = NULL; /* * We need to save the IP options in case a protocol wants to respond * to an incoming packet over the same route if the packet got here * using IP source routing. This allows connection establishment and * maintenance when the remote end is on a network that is not known * to us. */ static int ip_nhops = 0; static struct ip_srcrt { struct in_addr dst; /* final destination */ char nop; /* one NOP to align */ char srcopt[IPOPT_OFFSET + 1]; /* OPTVAL, OLEN and OFFSET */ struct in_addr route[MAX_IPOPTLEN/sizeof(struct in_addr)]; } ip_srcrt; struct sockaddr_in *ip_fw_fwd_addr; static void save_rte __P((u_char *, struct in_addr)); static int ip_dooptions __P((struct mbuf *)); static void ip_forward __P((struct mbuf *, int)); static void ip_freef __P((struct ipq *)); #ifdef IPDIVERT static struct mbuf *ip_reass __P((struct mbuf *, struct ipq *, struct ipq *, u_int32_t *, u_int16_t *)); #else static struct mbuf *ip_reass __P((struct mbuf *, struct ipq *, struct ipq *)); #endif static struct in_ifaddr *ip_rtaddr __P((struct in_addr)); static void ipintr __P((void)); /* * IP initialization: fill in IP protocol switch table. * All protocols not implemented in kernel go to raw IP protocol handler. */ void ip_init() { register struct ipprotosw *pr; register int i; TAILQ_INIT(&in_ifaddrhead); pr = (struct ipprotosw *)pffindproto(PF_INET, IPPROTO_RAW, SOCK_RAW); if (pr == 0) panic("ip_init"); for (i = 0; i < IPPROTO_MAX; i++) ip_protox[i] = pr - inetsw; for (pr = (struct ipprotosw *)inetdomain.dom_protosw; pr < (struct ipprotosw *)inetdomain.dom_protoswNPROTOSW; pr++) if (pr->pr_domain->dom_family == PF_INET && pr->pr_protocol && pr->pr_protocol != IPPROTO_RAW) ip_protox[pr->pr_protocol] = pr - inetsw; for (i = 0; i < IPREASS_NHASH; i++) ipq[i].next = ipq[i].prev = &ipq[i]; maxnipq = nmbclusters/4; ip_id = time_second & 0xffff; ipintrq.ifq_maxlen = ipqmaxlen; register_netisr(NETISR_IP, ipintr); } static struct sockaddr_in ipaddr = { sizeof(ipaddr), AF_INET }; static struct route ipforward_rt; /* * Ip input routine. Checksum and byte swap header. If fragmented * try to reassemble. Process options. Pass to next level. */ void ip_input(struct mbuf *m) { struct ip *ip; struct ipq *fp; struct in_ifaddr *ia; int i, hlen, mff; u_short sum; u_int16_t divert_cookie; /* firewall cookie */ #ifdef IPDIVERT u_int32_t divert_info = 0; /* packet divert/tee info */ #endif struct ip_fw_chain *rule = NULL; #ifdef IPDIVERT /* Get and reset firewall cookie */ divert_cookie = ip_divert_cookie; ip_divert_cookie = 0; #else divert_cookie = 0; #endif #if defined(IPFIREWALL) && defined(DUMMYNET) /* * dummynet packet are prepended a vestigial mbuf with * m_type = MT_DUMMYNET and m_data pointing to the matching * rule. */ if (m->m_type == MT_DUMMYNET) { rule = (struct ip_fw_chain *)(m->m_data) ; m = m->m_next ; ip = mtod(m, struct ip *); hlen = IP_VHL_HL(ip->ip_vhl) << 2; goto iphack ; } else rule = NULL ; #endif #ifdef DIAGNOSTIC if (m == NULL || (m->m_flags & M_PKTHDR) == 0) panic("ip_input no HDR"); #endif ipstat.ips_total++; if (m->m_pkthdr.len < sizeof(struct ip)) goto tooshort; if (m->m_len < sizeof (struct ip) && (m = m_pullup(m, sizeof (struct ip))) == 0) { ipstat.ips_toosmall++; return; } ip = mtod(m, struct ip *); if (IP_VHL_V(ip->ip_vhl) != IPVERSION) { ipstat.ips_badvers++; goto bad; } hlen = IP_VHL_HL(ip->ip_vhl) << 2; if (hlen < sizeof(struct ip)) { /* minimum header length */ ipstat.ips_badhlen++; goto bad; } if (hlen > m->m_len) { if ((m = m_pullup(m, hlen)) == 0) { ipstat.ips_badhlen++; return; } ip = mtod(m, struct ip *); } if (m->m_pkthdr.csum_flags & CSUM_IP_CHECKED) { sum = !(m->m_pkthdr.csum_flags & CSUM_IP_VALID); } else { if (hlen == sizeof(struct ip)) { sum = in_cksum_hdr(ip); } else { sum = in_cksum(m, hlen); } } if (sum) { ipstat.ips_badsum++; goto bad; } /* * Convert fields to host representation. */ NTOHS(ip->ip_len); if (ip->ip_len < hlen) { ipstat.ips_badlen++; goto bad; } NTOHS(ip->ip_id); NTOHS(ip->ip_off); /* * Check that the amount of data in the buffers * is as at least much as the IP header would have us expect. * Trim mbufs if longer than we expect. * Drop packet if shorter than we expect. */ if (m->m_pkthdr.len < ip->ip_len) { tooshort: ipstat.ips_tooshort++; goto bad; } if (m->m_pkthdr.len > ip->ip_len) { if (m->m_len == m->m_pkthdr.len) { m->m_len = ip->ip_len; m->m_pkthdr.len = ip->ip_len; } else m_adj(m, ip->ip_len - m->m_pkthdr.len); } /* * IpHack's section. * Right now when no processing on packet has done * and it is still fresh out of network we do our black * deals with it. * - Firewall: deny/allow/divert * - Xlate: translate packet's addr/port (NAT). * - Pipe: pass pkt through dummynet. * - Wrap: fake packet's addr/port * - Encapsulate: put it in another IP and send out. */ #if defined(IPFIREWALL) && defined(DUMMYNET) iphack: #endif /* * Check if we want to allow this packet to be processed. * Consider it to be bad if not. */ if (fr_checkp) { struct mbuf *m1 = m; if ((*fr_checkp)(ip, hlen, m->m_pkthdr.rcvif, 0, &m1) || !m1) return; ip = mtod(m = m1, struct ip *); } if (fw_enable && ip_fw_chk_ptr) { #ifdef IPFIREWALL_FORWARD /* * If we've been forwarded from the output side, then * skip the firewall a second time */ if (ip_fw_fwd_addr) goto ours; #endif /* IPFIREWALL_FORWARD */ /* * See the comment in ip_output for the return values * produced by the firewall. */ i = (*ip_fw_chk_ptr)(&ip, hlen, NULL, &divert_cookie, &m, &rule, &ip_fw_fwd_addr); if (m == NULL) /* Packet discarded by firewall */ return; if (i == 0 && ip_fw_fwd_addr == NULL) /* common case */ goto pass; #ifdef DUMMYNET if ((i & IP_FW_PORT_DYNT_FLAG) != 0) { /* Send packet to the appropriate pipe */ dummynet_io(i&0xffff,DN_TO_IP_IN,m,NULL,NULL,0, rule, 0); return; } #endif #ifdef IPDIVERT if (i != 0 && (i & IP_FW_PORT_DYNT_FLAG) == 0) { /* Divert or tee packet */ divert_info = i; goto ours; } #endif #ifdef IPFIREWALL_FORWARD if (i == 0 && ip_fw_fwd_addr != NULL) goto pass; #endif /* * if we get here, the packet must be dropped */ m_freem(m); return; } pass: /* * Process options and, if not destined for us, * ship it on. ip_dooptions returns 1 when an * error was detected (causing an icmp message * to be sent and the original packet to be freed). */ ip_nhops = 0; /* for source routed packets */ if (hlen > sizeof (struct ip) && ip_dooptions(m)) { #ifdef IPFIREWALL_FORWARD ip_fw_fwd_addr = NULL; #endif return; } /* greedy RSVP, snatches any PATH packet of the RSVP protocol and no * matter if it is destined to another node, or whether it is * a multicast one, RSVP wants it! and prevents it from being forwarded * anywhere else. Also checks if the rsvp daemon is running before * grabbing the packet. */ if (rsvp_on && ip->ip_p==IPPROTO_RSVP) goto ours; /* * Check our list of addresses, to see if the packet is for us. * If we don't have any addresses, assume any unicast packet * we receive might be for us (and let the upper layers deal * with it). */ if (TAILQ_EMPTY(&in_ifaddrhead) && (m->m_flags & (M_MCAST|M_BCAST)) == 0) goto ours; for (ia = TAILQ_FIRST(&in_ifaddrhead); ia; ia = TAILQ_NEXT(ia, ia_link)) { #define satosin(sa) ((struct sockaddr_in *)(sa)) #ifdef BOOTP_COMPAT if (IA_SIN(ia)->sin_addr.s_addr == INADDR_ANY) goto ours; #endif #ifdef IPFIREWALL_FORWARD /* * If the addr to forward to is one of ours, we pretend to * be the destination for this packet. */ if (ip_fw_fwd_addr == NULL) { if (IA_SIN(ia)->sin_addr.s_addr == ip->ip_dst.s_addr) goto ours; } else if (IA_SIN(ia)->sin_addr.s_addr == ip_fw_fwd_addr->sin_addr.s_addr) goto ours; #else if (IA_SIN(ia)->sin_addr.s_addr == ip->ip_dst.s_addr) goto ours; #endif if (ia->ia_ifp && ia->ia_ifp->if_flags & IFF_BROADCAST) { if (satosin(&ia->ia_broadaddr)->sin_addr.s_addr == ip->ip_dst.s_addr) goto ours; if (ip->ip_dst.s_addr == ia->ia_netbroadcast.s_addr) goto ours; } } if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr))) { struct in_multi *inm; if (ip_mrouter) { /* * If we are acting as a multicast router, all * incoming multicast packets are passed to the * kernel-level multicast forwarding function. * The packet is returned (relatively) intact; if * ip_mforward() returns a non-zero value, the packet * must be discarded, else it may be accepted below. * * (The IP ident field is put in the same byte order * as expected when ip_mforward() is called from * ip_output().) */ ip->ip_id = htons(ip->ip_id); if (ip_mforward(ip, m->m_pkthdr.rcvif, m, 0) != 0) { ipstat.ips_cantforward++; m_freem(m); return; } ip->ip_id = ntohs(ip->ip_id); /* * The process-level routing demon needs to receive * all multicast IGMP packets, whether or not this * host belongs to their destination groups. */ if (ip->ip_p == IPPROTO_IGMP) goto ours; ipstat.ips_forward++; } /* * See if we belong to the destination multicast group on the * arrival interface. */ IN_LOOKUP_MULTI(ip->ip_dst, m->m_pkthdr.rcvif, inm); if (inm == NULL) { ipstat.ips_notmember++; m_freem(m); return; } goto ours; } if (ip->ip_dst.s_addr == (u_long)INADDR_BROADCAST) goto ours; if (ip->ip_dst.s_addr == INADDR_ANY) goto ours; #if defined(NFAITH) && 0 < NFAITH /* * FAITH(Firewall Aided Internet Translator) */ if (m->m_pkthdr.rcvif && m->m_pkthdr.rcvif->if_type == IFT_FAITH) { if (ip_keepfaith) { if (ip->ip_p == IPPROTO_TCP || ip->ip_p == IPPROTO_ICMP) goto ours; } m_freem(m); return; } #endif /* * Not for us; forward if possible and desirable. */ if (ipforwarding == 0) { ipstat.ips_cantforward++; m_freem(m); } else ip_forward(m, 0); #ifdef IPFIREWALL_FORWARD ip_fw_fwd_addr = NULL; #endif return; ours: /* * If offset or IP_MF are set, must reassemble. * Otherwise, nothing need be done. * (We could look in the reassembly queue to see * if the packet was previously fragmented, * but it's not worth the time; just let them time out.) */ if (ip->ip_off & (IP_MF | IP_OFFMASK | IP_RF)) { #if 0 /* * Reassembly should be able to treat a mbuf cluster, for later * operation of contiguous protocol headers on the cluster. (KAME) */ if (m->m_flags & M_EXT) { /* XXX */ if ((m = m_pullup(m, hlen)) == 0) { ipstat.ips_toosmall++; #ifdef IPFIREWALL_FORWARD ip_fw_fwd_addr = NULL; #endif return; } ip = mtod(m, struct ip *); } #endif sum = IPREASS_HASH(ip->ip_src.s_addr, ip->ip_id); /* * Look for queue of fragments * of this datagram. */ for (fp = ipq[sum].next; fp != &ipq[sum]; fp = fp->next) if (ip->ip_id == fp->ipq_id && ip->ip_src.s_addr == fp->ipq_src.s_addr && ip->ip_dst.s_addr == fp->ipq_dst.s_addr && ip->ip_p == fp->ipq_p) goto found; fp = 0; /* check if there's a place for the new queue */ if (nipq > maxnipq) { /* * drop something from the tail of the current queue * before proceeding further */ if (ipq[sum].prev == &ipq[sum]) { /* gak */ for (i = 0; i < IPREASS_NHASH; i++) { if (ipq[i].prev != &ipq[i]) { ip_freef(ipq[i].prev); break; } } } else ip_freef(ipq[sum].prev); } found: /* * Adjust ip_len to not reflect header, * set ip_mff if more fragments are expected, * convert offset of this to bytes. */ ip->ip_len -= hlen; mff = (ip->ip_off & IP_MF) != 0; if (mff) { /* * Make sure that fragments have a data length * that's a non-zero multiple of 8 bytes. */ if (ip->ip_len == 0 || (ip->ip_len & 0x7) != 0) { ipstat.ips_toosmall++; /* XXX */ goto bad; } m->m_flags |= M_FRAG; } ip->ip_off <<= 3; /* * If datagram marked as having more fragments * or if this is not the first fragment, * attempt reassembly; if it succeeds, proceed. */ if (mff || ip->ip_off) { ipstat.ips_fragments++; m->m_pkthdr.header = ip; #ifdef IPDIVERT m = ip_reass(m, fp, &ipq[sum], &divert_info, &divert_cookie); #else m = ip_reass(m, fp, &ipq[sum]); #endif if (m == 0) { #ifdef IPFIREWALL_FORWARD ip_fw_fwd_addr = NULL; #endif return; } /* Get the length of the reassembled packets header */ hlen = IP_VHL_HL(ip->ip_vhl) << 2; ipstat.ips_reassembled++; ip = mtod(m, struct ip *); #ifdef IPDIVERT /* Restore original checksum before diverting packet */ if (divert_info != 0) { ip->ip_len += hlen; HTONS(ip->ip_len); HTONS(ip->ip_off); HTONS(ip->ip_id); ip->ip_sum = 0; ip->ip_sum = in_cksum_hdr(ip); NTOHS(ip->ip_id); NTOHS(ip->ip_off); NTOHS(ip->ip_len); ip->ip_len -= hlen; } #endif } else if (fp) ip_freef(fp); } else ip->ip_len -= hlen; #ifdef IPDIVERT /* * Divert or tee packet to the divert protocol if required. * * If divert_info is zero then cookie should be too, so we shouldn't * need to clear them here. Assume divert_packet() does so also. */ if (divert_info != 0) { struct mbuf *clone = NULL; /* Clone packet if we're doing a 'tee' */ if ((divert_info & IP_FW_PORT_TEE_FLAG) != 0) clone = m_dup(m, M_DONTWAIT); /* Restore packet header fields to original values */ ip->ip_len += hlen; HTONS(ip->ip_len); HTONS(ip->ip_off); HTONS(ip->ip_id); /* Deliver packet to divert input routine */ ip_divert_cookie = divert_cookie; divert_packet(m, 1, divert_info & 0xffff); ipstat.ips_delivered++; /* If 'tee', continue with original packet */ if (clone == NULL) return; m = clone; ip = mtod(m, struct ip *); } #endif /* * Switch out to protocol's input routine. */ ipstat.ips_delivered++; { int off = hlen, nh = ip->ip_p; (*inetsw[ip_protox[ip->ip_p]].pr_input)(m, off, nh); #ifdef IPFIREWALL_FORWARD ip_fw_fwd_addr = NULL; /* tcp needed it */ #endif return; } bad: #ifdef IPFIREWALL_FORWARD ip_fw_fwd_addr = NULL; #endif m_freem(m); } /* * IP software interrupt routine - to go away sometime soon */ static void ipintr(void) { int s; struct mbuf *m; while(1) { s = splimp(); IF_DEQUEUE(&ipintrq, m); splx(s); if (m == 0) return; ip_input(m); } } /* * Take incoming datagram fragment and try to reassemble it into * whole datagram. If a chain for reassembly of this datagram already * exists, then it is given as fp; otherwise have to make a chain. * * When IPDIVERT enabled, keep additional state with each packet that * tells us if we need to divert or tee the packet we're building. */ static struct mbuf * #ifdef IPDIVERT ip_reass(m, fp, where, divinfo, divcookie) #else ip_reass(m, fp, where) #endif register struct mbuf *m; register struct ipq *fp; struct ipq *where; #ifdef IPDIVERT u_int32_t *divinfo; u_int16_t *divcookie; #endif { struct ip *ip = mtod(m, struct ip *); register struct mbuf *p = 0, *q, *nq; struct mbuf *t; int hlen = IP_VHL_HL(ip->ip_vhl) << 2; int i, next; /* * Presence of header sizes in mbufs * would confuse code below. */ m->m_data += hlen; m->m_len -= hlen; /* * If first fragment to arrive, create a reassembly queue. */ if (fp == 0) { if ((t = m_get(M_DONTWAIT, MT_FTABLE)) == NULL) goto dropfrag; fp = mtod(t, struct ipq *); insque(fp, where); nipq++; fp->ipq_ttl = IPFRAGTTL; fp->ipq_p = ip->ip_p; fp->ipq_id = ip->ip_id; fp->ipq_src = ip->ip_src; fp->ipq_dst = ip->ip_dst; fp->ipq_frags = m; m->m_nextpkt = NULL; #ifdef IPDIVERT fp->ipq_div_info = 0; fp->ipq_div_cookie = 0; #endif goto inserted; } #define GETIP(m) ((struct ip*)((m)->m_pkthdr.header)) /* * Find a segment which begins after this one does. */ for (p = NULL, q = fp->ipq_frags; q; p = q, q = q->m_nextpkt) if (GETIP(q)->ip_off > ip->ip_off) break; /* * If there is a preceding segment, it may provide some of * our data already. If so, drop the data from the incoming * segment. If it provides all of our data, drop us, otherwise * stick new segment in the proper place. * * If some of the data is dropped from the the preceding * segment, then it's checksum is invalidated. */ if (p) { i = GETIP(p)->ip_off + GETIP(p)->ip_len - ip->ip_off; if (i > 0) { if (i >= ip->ip_len) goto dropfrag; m_adj(m, i); m->m_pkthdr.csum_flags = 0; ip->ip_off += i; ip->ip_len -= i; } m->m_nextpkt = p->m_nextpkt; p->m_nextpkt = m; } else { m->m_nextpkt = fp->ipq_frags; fp->ipq_frags = m; } /* * While we overlap succeeding segments trim them or, * if they are completely covered, dequeue them. */ for (; q != NULL && ip->ip_off + ip->ip_len > GETIP(q)->ip_off; q = nq) { i = (ip->ip_off + ip->ip_len) - GETIP(q)->ip_off; if (i < GETIP(q)->ip_len) { GETIP(q)->ip_len -= i; GETIP(q)->ip_off += i; m_adj(q, i); q->m_pkthdr.csum_flags = 0; break; } nq = q->m_nextpkt; m->m_nextpkt = nq; m_freem(q); } inserted: #ifdef IPDIVERT /* * Transfer firewall instructions to the fragment structure. * Any fragment diverting causes the whole packet to divert. */ fp->ipq_div_info = *divinfo; fp->ipq_div_cookie = *divcookie; *divinfo = 0; *divcookie = 0; #endif /* * Check for complete reassembly. */ next = 0; for (p = NULL, q = fp->ipq_frags; q; p = q, q = q->m_nextpkt) { if (GETIP(q)->ip_off != next) return (0); next += GETIP(q)->ip_len; } /* Make sure the last packet didn't have the IP_MF flag */ if (p->m_flags & M_FRAG) return (0); /* * Reassembly is complete. Make sure the packet is a sane size. */ q = fp->ipq_frags; ip = GETIP(q); if (next + (IP_VHL_HL(ip->ip_vhl) << 2) > IP_MAXPACKET) { ipstat.ips_toolong++; ip_freef(fp); return (0); } /* * Concatenate fragments. */ m = q; t = m->m_next; m->m_next = 0; m_cat(m, t); nq = q->m_nextpkt; q->m_nextpkt = 0; for (q = nq; q != NULL; q = nq) { nq = q->m_nextpkt; q->m_nextpkt = NULL; m_cat(m, q); m->m_pkthdr.csum_flags &= q->m_pkthdr.csum_flags; m->m_pkthdr.csum_data += q->m_pkthdr.csum_data; } #ifdef IPDIVERT /* * Extract firewall instructions from the fragment structure. */ *divinfo = fp->ipq_div_info; *divcookie = fp->ipq_div_cookie; #endif /* * Create header for new ip packet by * modifying header of first packet; * dequeue and discard fragment reassembly header. * Make header visible. */ ip->ip_len = next; ip->ip_src = fp->ipq_src; ip->ip_dst = fp->ipq_dst; remque(fp); nipq--; (void) m_free(dtom(fp)); m->m_len += (IP_VHL_HL(ip->ip_vhl) << 2); m->m_data -= (IP_VHL_HL(ip->ip_vhl) << 2); /* some debugging cruft by sklower, below, will go away soon */ if (m->m_flags & M_PKTHDR) { /* XXX this should be done elsewhere */ register int plen = 0; for (t = m; t; t = t->m_next) plen += t->m_len; m->m_pkthdr.len = plen; } return (m); dropfrag: #ifdef IPDIVERT *divinfo = 0; *divcookie = 0; #endif ipstat.ips_fragdropped++; m_freem(m); return (0); #undef GETIP } /* * Free a fragment reassembly header and all * associated datagrams. */ static void ip_freef(fp) struct ipq *fp; { register struct mbuf *q; while (fp->ipq_frags) { q = fp->ipq_frags; fp->ipq_frags = q->m_nextpkt; m_freem(q); } remque(fp); (void) m_free(dtom(fp)); nipq--; } /* * IP timer processing; * if a timer expires on a reassembly * queue, discard it. */ void ip_slowtimo() { register struct ipq *fp; int s = splnet(); int i; for (i = 0; i < IPREASS_NHASH; i++) { fp = ipq[i].next; if (fp == 0) continue; while (fp != &ipq[i]) { --fp->ipq_ttl; fp = fp->next; if (fp->prev->ipq_ttl == 0) { ipstat.ips_fragtimeout++; ip_freef(fp->prev); } } } ipflow_slowtimo(); splx(s); } /* * Drain off all datagram fragments. */ void ip_drain() { int i; for (i = 0; i < IPREASS_NHASH; i++) { while (ipq[i].next != &ipq[i]) { ipstat.ips_fragdropped++; ip_freef(ipq[i].next); } } in_rtqdrain(); } /* * Do option processing on a datagram, * possibly discarding it if bad options are encountered, * or forwarding it if source-routed. * Returns 1 if packet has been forwarded/freed, * 0 if the packet should be processed further. */ static int ip_dooptions(m) struct mbuf *m; { register struct ip *ip = mtod(m, struct ip *); register u_char *cp; register struct ip_timestamp *ipt; register struct in_ifaddr *ia; int opt, optlen, cnt, off, code, type = ICMP_PARAMPROB, forward = 0; struct in_addr *sin, dst; n_time ntime; dst = ip->ip_dst; cp = (u_char *)(ip + 1); cnt = (IP_VHL_HL(ip->ip_vhl) << 2) - sizeof (struct ip); for (; cnt > 0; cnt -= optlen, cp += optlen) { opt = cp[IPOPT_OPTVAL]; if (opt == IPOPT_EOL) break; if (opt == IPOPT_NOP) optlen = 1; else { if (cnt < IPOPT_OLEN + sizeof(*cp)) { code = &cp[IPOPT_OLEN] - (u_char *)ip; goto bad; } optlen = cp[IPOPT_OLEN]; if (optlen < IPOPT_OLEN + sizeof(*cp) || optlen > cnt) { code = &cp[IPOPT_OLEN] - (u_char *)ip; goto bad; } } switch (opt) { default: break; /* * Source routing with record. * Find interface with current destination address. * If none on this machine then drop if strictly routed, * or do nothing if loosely routed. * Record interface address and bring up next address * component. If strictly routed make sure next * address is on directly accessible net. */ case IPOPT_LSRR: case IPOPT_SSRR: if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) { code = &cp[IPOPT_OFFSET] - (u_char *)ip; goto bad; } ipaddr.sin_addr = ip->ip_dst; ia = (struct in_ifaddr *) ifa_ifwithaddr((struct sockaddr *)&ipaddr); if (ia == 0) { if (opt == IPOPT_SSRR) { type = ICMP_UNREACH; code = ICMP_UNREACH_SRCFAIL; goto bad; } if (!ip_dosourceroute) goto nosourcerouting; /* * Loose routing, and not at next destination * yet; nothing to do except forward. */ break; } off--; /* 0 origin */ if (off > optlen - (int)sizeof(struct in_addr)) { /* * End of source route. Should be for us. */ if (!ip_acceptsourceroute) goto nosourcerouting; save_rte(cp, ip->ip_src); break; } if (!ip_dosourceroute) { if (ipforwarding) { char buf[16]; /* aaa.bbb.ccc.ddd\0 */ /* * Acting as a router, so generate ICMP */ nosourcerouting: strcpy(buf, inet_ntoa(ip->ip_dst)); log(LOG_WARNING, "attempted source route from %s to %s\n", inet_ntoa(ip->ip_src), buf); type = ICMP_UNREACH; code = ICMP_UNREACH_SRCFAIL; goto bad; } else { /* * Not acting as a router, so silently drop. */ ipstat.ips_cantforward++; m_freem(m); return (1); } } /* * locate outgoing interface */ (void)memcpy(&ipaddr.sin_addr, cp + off, sizeof(ipaddr.sin_addr)); if (opt == IPOPT_SSRR) { #define INA struct in_ifaddr * #define SA struct sockaddr * if ((ia = (INA)ifa_ifwithdstaddr((SA)&ipaddr)) == 0) ia = (INA)ifa_ifwithnet((SA)&ipaddr); } else ia = ip_rtaddr(ipaddr.sin_addr); if (ia == 0) { type = ICMP_UNREACH; code = ICMP_UNREACH_SRCFAIL; goto bad; } ip->ip_dst = ipaddr.sin_addr; (void)memcpy(cp + off, &(IA_SIN(ia)->sin_addr), sizeof(struct in_addr)); cp[IPOPT_OFFSET] += sizeof(struct in_addr); /* * Let ip_intr's mcast routing check handle mcast pkts */ forward = !IN_MULTICAST(ntohl(ip->ip_dst.s_addr)); break; case IPOPT_RR: if (optlen < IPOPT_OFFSET + sizeof(*cp)) { code = &cp[IPOPT_OFFSET] - (u_char *)ip; goto bad; } if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) { code = &cp[IPOPT_OFFSET] - (u_char *)ip; goto bad; } /* * If no space remains, ignore. */ off--; /* 0 origin */ if (off > optlen - (int)sizeof(struct in_addr)) break; (void)memcpy(&ipaddr.sin_addr, &ip->ip_dst, sizeof(ipaddr.sin_addr)); /* * locate outgoing interface; if we're the destination, * use the incoming interface (should be same). */ if ((ia = (INA)ifa_ifwithaddr((SA)&ipaddr)) == 0 && (ia = ip_rtaddr(ipaddr.sin_addr)) == 0) { type = ICMP_UNREACH; code = ICMP_UNREACH_HOST; goto bad; } (void)memcpy(cp + off, &(IA_SIN(ia)->sin_addr), sizeof(struct in_addr)); cp[IPOPT_OFFSET] += sizeof(struct in_addr); break; case IPOPT_TS: code = cp - (u_char *)ip; ipt = (struct ip_timestamp *)cp; if (ipt->ipt_len < 5) goto bad; if (ipt->ipt_ptr > ipt->ipt_len - (int)sizeof(int32_t)) { if (++ipt->ipt_oflw == 0) goto bad; break; } sin = (struct in_addr *)(cp + ipt->ipt_ptr - 1); switch (ipt->ipt_flg) { case IPOPT_TS_TSONLY: break; case IPOPT_TS_TSANDADDR: if (ipt->ipt_ptr - 1 + sizeof(n_time) + sizeof(struct in_addr) > ipt->ipt_len) goto bad; ipaddr.sin_addr = dst; ia = (INA)ifaof_ifpforaddr((SA)&ipaddr, m->m_pkthdr.rcvif); if (ia == 0) continue; (void)memcpy(sin, &IA_SIN(ia)->sin_addr, sizeof(struct in_addr)); ipt->ipt_ptr += sizeof(struct in_addr); break; case IPOPT_TS_PRESPEC: if (ipt->ipt_ptr - 1 + sizeof(n_time) + sizeof(struct in_addr) > ipt->ipt_len) goto bad; (void)memcpy(&ipaddr.sin_addr, sin, sizeof(struct in_addr)); if (ifa_ifwithaddr((SA)&ipaddr) == 0) continue; ipt->ipt_ptr += sizeof(struct in_addr); break; default: goto bad; } ntime = iptime(); (void)memcpy(cp + ipt->ipt_ptr - 1, &ntime, sizeof(n_time)); ipt->ipt_ptr += sizeof(n_time); } } if (forward && ipforwarding) { ip_forward(m, 1); return (1); } return (0); bad: ip->ip_len -= IP_VHL_HL(ip->ip_vhl) << 2; /* XXX icmp_error adds in hdr length */ icmp_error(m, type, code, 0, 0); ipstat.ips_badoptions++; return (1); } /* * Given address of next destination (final or next hop), * return internet address info of interface to be used to get there. */ static struct in_ifaddr * ip_rtaddr(dst) struct in_addr dst; { register struct sockaddr_in *sin; sin = (struct sockaddr_in *) &ipforward_rt.ro_dst; if (ipforward_rt.ro_rt == 0 || dst.s_addr != sin->sin_addr.s_addr) { if (ipforward_rt.ro_rt) { RTFREE(ipforward_rt.ro_rt); ipforward_rt.ro_rt = 0; } sin->sin_family = AF_INET; sin->sin_len = sizeof(*sin); sin->sin_addr = dst; rtalloc_ign(&ipforward_rt, RTF_PRCLONING); } if (ipforward_rt.ro_rt == 0) return ((struct in_ifaddr *)0); return ((struct in_ifaddr *) ipforward_rt.ro_rt->rt_ifa); } /* * Save incoming source route for use in replies, * to be picked up later by ip_srcroute if the receiver is interested. */ void save_rte(option, dst) u_char *option; struct in_addr dst; { unsigned olen; olen = option[IPOPT_OLEN]; #ifdef DIAGNOSTIC if (ipprintfs) printf("save_rte: olen %d\n", olen); #endif if (olen > sizeof(ip_srcrt) - (1 + sizeof(dst))) return; bcopy(option, ip_srcrt.srcopt, olen); ip_nhops = (olen - IPOPT_OFFSET - 1) / sizeof(struct in_addr); ip_srcrt.dst = dst; } /* * Retrieve incoming source route for use in replies, * in the same form used by setsockopt. * The first hop is placed before the options, will be removed later. */ struct mbuf * ip_srcroute() { register struct in_addr *p, *q; register struct mbuf *m; if (ip_nhops == 0) return ((struct mbuf *)0); m = m_get(M_DONTWAIT, MT_HEADER); if (m == 0) return ((struct mbuf *)0); #define OPTSIZ (sizeof(ip_srcrt.nop) + sizeof(ip_srcrt.srcopt)) /* length is (nhops+1)*sizeof(addr) + sizeof(nop + srcrt header) */ m->m_len = ip_nhops * sizeof(struct in_addr) + sizeof(struct in_addr) + OPTSIZ; #ifdef DIAGNOSTIC if (ipprintfs) printf("ip_srcroute: nhops %d mlen %d", ip_nhops, m->m_len); #endif /* * First save first hop for return route */ p = &ip_srcrt.route[ip_nhops - 1]; *(mtod(m, struct in_addr *)) = *p--; #ifdef DIAGNOSTIC if (ipprintfs) printf(" hops %lx", (u_long)ntohl(mtod(m, struct in_addr *)->s_addr)); #endif /* * Copy option fields and padding (nop) to mbuf. */ ip_srcrt.nop = IPOPT_NOP; ip_srcrt.srcopt[IPOPT_OFFSET] = IPOPT_MINOFF; (void)memcpy(mtod(m, caddr_t) + sizeof(struct in_addr), &ip_srcrt.nop, OPTSIZ); q = (struct in_addr *)(mtod(m, caddr_t) + sizeof(struct in_addr) + OPTSIZ); #undef OPTSIZ /* * Record return path as an IP source route, * reversing the path (pointers are now aligned). */ while (p >= ip_srcrt.route) { #ifdef DIAGNOSTIC if (ipprintfs) printf(" %lx", (u_long)ntohl(q->s_addr)); #endif *q++ = *p--; } /* * Last hop goes to final destination. */ *q = ip_srcrt.dst; #ifdef DIAGNOSTIC if (ipprintfs) printf(" %lx\n", (u_long)ntohl(q->s_addr)); #endif return (m); } /* * Strip out IP options, at higher * level protocol in the kernel. * Second argument is buffer to which options * will be moved, and return value is their length. * XXX should be deleted; last arg currently ignored. */ void ip_stripoptions(m, mopt) register struct mbuf *m; struct mbuf *mopt; { register int i; struct ip *ip = mtod(m, struct ip *); register caddr_t opts; int olen; olen = (IP_VHL_HL(ip->ip_vhl) << 2) - sizeof (struct ip); opts = (caddr_t)(ip + 1); i = m->m_len - (sizeof (struct ip) + olen); bcopy(opts + olen, opts, (unsigned)i); m->m_len -= olen; if (m->m_flags & M_PKTHDR) m->m_pkthdr.len -= olen; ip->ip_vhl = IP_MAKE_VHL(IPVERSION, sizeof(struct ip) >> 2); } u_char inetctlerrmap[PRC_NCMDS] = { 0, 0, 0, 0, 0, EMSGSIZE, EHOSTDOWN, EHOSTUNREACH, EHOSTUNREACH, EHOSTUNREACH, ECONNREFUSED, ECONNREFUSED, EMSGSIZE, EHOSTUNREACH, 0, 0, 0, 0, 0, 0, ENOPROTOOPT }; /* * Forward a packet. If some error occurs return the sender * an icmp packet. Note we can't always generate a meaningful * icmp message because icmp doesn't have a large enough repertoire * of codes and types. * * If not forwarding, just drop the packet. This could be confusing * if ipforwarding was zero but some routing protocol was advancing * us as a gateway to somewhere. However, we must let the routing * protocol deal with that. * * The srcrt parameter indicates whether the packet is being forwarded * via a source route. */ static void ip_forward(m, srcrt) struct mbuf *m; int srcrt; { register struct ip *ip = mtod(m, struct ip *); register struct sockaddr_in *sin; register struct rtentry *rt; int error, type = 0, code = 0; struct mbuf *mcopy; n_long dest; struct ifnet *destifp; #ifdef IPSEC struct ifnet dummyifp; #endif dest = 0; #ifdef DIAGNOSTIC if (ipprintfs) printf("forward: src %lx dst %lx ttl %x\n", (u_long)ip->ip_src.s_addr, (u_long)ip->ip_dst.s_addr, ip->ip_ttl); #endif if (m->m_flags & (M_BCAST|M_MCAST) || in_canforward(ip->ip_dst) == 0) { ipstat.ips_cantforward++; m_freem(m); return; } HTONS(ip->ip_id); #ifdef IPSTEALTH if (!ipstealth) { #endif if (ip->ip_ttl <= IPTTLDEC) { icmp_error(m, ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS, dest, 0); return; } ip->ip_ttl -= IPTTLDEC; #ifdef IPSTEALTH } #endif sin = (struct sockaddr_in *)&ipforward_rt.ro_dst; if ((rt = ipforward_rt.ro_rt) == 0 || ip->ip_dst.s_addr != sin->sin_addr.s_addr) { if (ipforward_rt.ro_rt) { RTFREE(ipforward_rt.ro_rt); ipforward_rt.ro_rt = 0; } sin->sin_family = AF_INET; sin->sin_len = sizeof(*sin); sin->sin_addr = ip->ip_dst; rtalloc_ign(&ipforward_rt, RTF_PRCLONING); if (ipforward_rt.ro_rt == 0) { icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_HOST, dest, 0); return; } rt = ipforward_rt.ro_rt; } /* * Save at most 64 bytes of the packet in case * we need to generate an ICMP message to the src. */ mcopy = m_copy(m, 0, imin((int)ip->ip_len, 64)); /* * If forwarding packet using same interface that it came in on, * perhaps should send a redirect to sender to shortcut a hop. * Only send redirect if source is sending directly to us, * and if packet was not source routed (or has any options). * Also, don't send redirect if forwarding using a default route * or a route modified by a redirect. */ #define satosin(sa) ((struct sockaddr_in *)(sa)) if (rt->rt_ifp == m->m_pkthdr.rcvif && (rt->rt_flags & (RTF_DYNAMIC|RTF_MODIFIED)) == 0 && satosin(rt_key(rt))->sin_addr.s_addr != 0 && ipsendredirects && !srcrt) { #define RTA(rt) ((struct in_ifaddr *)(rt->rt_ifa)) u_long src = ntohl(ip->ip_src.s_addr); if (RTA(rt) && (src & RTA(rt)->ia_subnetmask) == RTA(rt)->ia_subnet) { if (rt->rt_flags & RTF_GATEWAY) dest = satosin(rt->rt_gateway)->sin_addr.s_addr; else dest = ip->ip_dst.s_addr; /* Router requirements says to only send host redirects */ type = ICMP_REDIRECT; code = ICMP_REDIRECT_HOST; #ifdef DIAGNOSTIC if (ipprintfs) printf("redirect (%d) to %lx\n", code, (u_long)dest); #endif } } error = ip_output(m, (struct mbuf *)0, &ipforward_rt, IP_FORWARDING, 0); if (error) ipstat.ips_cantforward++; else { ipstat.ips_forward++; if (type) ipstat.ips_redirectsent++; else { if (mcopy) { ipflow_create(&ipforward_rt, mcopy); m_freem(mcopy); } return; } } if (mcopy == NULL) return; destifp = NULL; switch (error) { case 0: /* forwarded, but need redirect */ /* type, code set above */ break; case ENETUNREACH: /* shouldn't happen, checked above */ case EHOSTUNREACH: case ENETDOWN: case EHOSTDOWN: default: type = ICMP_UNREACH; code = ICMP_UNREACH_HOST; break; case EMSGSIZE: type = ICMP_UNREACH; code = ICMP_UNREACH_NEEDFRAG; #ifndef IPSEC if (ipforward_rt.ro_rt) destifp = ipforward_rt.ro_rt->rt_ifp; #else /* * If the packet is routed over IPsec tunnel, tell the * originator the tunnel MTU. * tunnel MTU = if MTU - sizeof(IP) - ESP/AH hdrsiz * XXX quickhack!!! */ if (ipforward_rt.ro_rt) { struct secpolicy *sp = NULL; int ipsecerror; int ipsechdr; struct route *ro; sp = ipsec4_getpolicybyaddr(mcopy, IPSEC_DIR_OUTBOUND, IP_FORWARDING, &ipsecerror); if (sp == NULL) destifp = ipforward_rt.ro_rt->rt_ifp; else { /* count IPsec header size */ ipsechdr = ipsec4_hdrsiz(mcopy, IPSEC_DIR_OUTBOUND, NULL); /* * find the correct route for outer IPv4 * header, compute tunnel MTU. * * XXX BUG ALERT * The "dummyifp" code relies upon the fact * that icmp_error() touches only ifp->if_mtu. */ /*XXX*/ destifp = NULL; if (sp->req != NULL && sp->req->sav != NULL && sp->req->sav->sah != NULL) { ro = &sp->req->sav->sah->sa_route; if (ro->ro_rt && ro->ro_rt->rt_ifp) { dummyifp.if_mtu = ro->ro_rt->rt_ifp->if_mtu; dummyifp.if_mtu -= ipsechdr; destifp = &dummyifp; } } key_freesp(sp); } } #endif /*IPSEC*/ ipstat.ips_cantfrag++; break; case ENOBUFS: type = ICMP_SOURCEQUENCH; code = 0; break; case EACCES: /* ipfw denied packet */ m_freem(mcopy); return; } icmp_error(mcopy, type, code, dest, destifp); } void ip_savecontrol(inp, mp, ip, m) register struct inpcb *inp; register struct mbuf **mp; register struct ip *ip; register struct mbuf *m; { if (inp->inp_socket->so_options & SO_TIMESTAMP) { struct timeval tv; microtime(&tv); *mp = sbcreatecontrol((caddr_t) &tv, sizeof(tv), SCM_TIMESTAMP, SOL_SOCKET); if (*mp) mp = &(*mp)->m_next; } if (inp->inp_flags & INP_RECVDSTADDR) { *mp = sbcreatecontrol((caddr_t) &ip->ip_dst, sizeof(struct in_addr), IP_RECVDSTADDR, IPPROTO_IP); if (*mp) mp = &(*mp)->m_next; } #ifdef notyet /* XXX * Moving these out of udp_input() made them even more broken * than they already were. */ /* options were tossed already */ if (inp->inp_flags & INP_RECVOPTS) { *mp = sbcreatecontrol((caddr_t) opts_deleted_above, sizeof(struct in_addr), IP_RECVOPTS, IPPROTO_IP); if (*mp) mp = &(*mp)->m_next; } /* ip_srcroute doesn't do what we want here, need to fix */ if (inp->inp_flags & INP_RECVRETOPTS) { *mp = sbcreatecontrol((caddr_t) ip_srcroute(), sizeof(struct in_addr), IP_RECVRETOPTS, IPPROTO_IP); if (*mp) mp = &(*mp)->m_next; } #endif if (inp->inp_flags & INP_RECVIF) { struct ifnet *ifp; struct sdlbuf { struct sockaddr_dl sdl; u_char pad[32]; } sdlbuf; struct sockaddr_dl *sdp; struct sockaddr_dl *sdl2 = &sdlbuf.sdl; if (((ifp = m->m_pkthdr.rcvif)) && ( ifp->if_index && (ifp->if_index <= if_index))) { sdp = (struct sockaddr_dl *)(ifnet_addrs [ifp->if_index - 1]->ifa_addr); /* * Change our mind and don't try copy. */ if ((sdp->sdl_family != AF_LINK) || (sdp->sdl_len > sizeof(sdlbuf))) { goto makedummy; } bcopy(sdp, sdl2, sdp->sdl_len); } else { makedummy: sdl2->sdl_len = offsetof(struct sockaddr_dl, sdl_data[0]); sdl2->sdl_family = AF_LINK; sdl2->sdl_index = 0; sdl2->sdl_nlen = sdl2->sdl_alen = sdl2->sdl_slen = 0; } *mp = sbcreatecontrol((caddr_t) sdl2, sdl2->sdl_len, IP_RECVIF, IPPROTO_IP); if (*mp) mp = &(*mp)->m_next; } } int ip_rsvp_init(struct socket *so) { if (so->so_type != SOCK_RAW || so->so_proto->pr_protocol != IPPROTO_RSVP) return EOPNOTSUPP; if (ip_rsvpd != NULL) return EADDRINUSE; ip_rsvpd = so; /* * This may seem silly, but we need to be sure we don't over-increment * the RSVP counter, in case something slips up. */ if (!ip_rsvp_on) { ip_rsvp_on = 1; rsvp_on++; } return 0; } int ip_rsvp_done(void) { ip_rsvpd = NULL; /* * This may seem silly, but we need to be sure we don't over-decrement * the RSVP counter, in case something slips up. */ if (ip_rsvp_on) { ip_rsvp_on = 0; rsvp_on--; } return 0; } Index: head/sys/netinet/ip_output.c =================================================================== --- head/sys/netinet/ip_output.c (revision 62586) +++ head/sys/netinet/ip_output.c (revision 62587) @@ -1,1882 +1,1881 @@ /* * Copyright (c) 1982, 1986, 1988, 1990, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)ip_output.c 8.3 (Berkeley) 1/21/94 * $FreeBSD$ */ #define _IP_VHL #include "opt_ipfw.h" #include "opt_ipdn.h" #include "opt_ipdivert.h" #include "opt_ipfilter.h" #include "opt_ipsec.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "faith.h" #ifdef vax #include #endif #include static MALLOC_DEFINE(M_IPMOPTS, "ip_moptions", "internet multicast options"); #ifdef IPSEC #include #include #ifdef IPSEC_DEBUG #include #else #define KEYDEBUG(lev,arg) #endif #endif /*IPSEC*/ #include #ifdef DUMMYNET #include #endif #ifdef IPFIREWALL_FORWARD_DEBUG #define print_ip(a) printf("%ld.%ld.%ld.%ld",(ntohl(a.s_addr)>>24)&0xFF,\ (ntohl(a.s_addr)>>16)&0xFF,\ (ntohl(a.s_addr)>>8)&0xFF,\ (ntohl(a.s_addr))&0xFF); #endif u_short ip_id; static struct mbuf *ip_insertoptions __P((struct mbuf *, struct mbuf *, int *)); static void ip_mloopback __P((struct ifnet *, struct mbuf *, struct sockaddr_in *, int)); static int ip_getmoptions __P((struct sockopt *, struct ip_moptions *)); static int ip_pcbopts __P((int, struct mbuf **, struct mbuf *)); static int ip_setmoptions __P((struct sockopt *, struct ip_moptions **)); int ip_optcopy __P((struct ip *, struct ip *)); extern int (*fr_checkp) __P((struct ip *, int, struct ifnet *, int, struct mbuf **)); extern struct protosw inetsw[]; /* * IP output. The packet in mbuf chain m contains a skeletal IP * header (with len, off, ttl, proto, tos, src, dst). * The mbuf chain containing the packet will be freed. * The mbuf opt, if present, will not be freed. */ int ip_output(m0, opt, ro, flags, imo) struct mbuf *m0; struct mbuf *opt; struct route *ro; int flags; struct ip_moptions *imo; { struct ip *ip, *mhip; struct ifnet *ifp; struct mbuf *m = m0; int hlen = sizeof (struct ip); int len, off, error = 0; struct sockaddr_in *dst; struct in_ifaddr *ia; int isbroadcast, sw_csum; #ifdef IPSEC struct route iproute; struct socket *so = NULL; struct secpolicy *sp = NULL; #endif u_int16_t divert_cookie; /* firewall cookie */ #ifdef IPFIREWALL_FORWARD int fwd_rewrite_src = 0; #endif struct ip_fw_chain *rule = NULL; #ifdef IPDIVERT /* Get and reset firewall cookie */ divert_cookie = ip_divert_cookie; ip_divert_cookie = 0; #else divert_cookie = 0; #endif - /* - * NOTE: If IP_SOCKINMRCVIF flag is set, 'socket *' is kept in - * m->m_pkthdr.rcvif for later IPSEC check. In this case, - * m->m_pkthdr will be NULL cleared after the contents is saved in - * 'so'. - * NULL clearance of rcvif should be natural because the packet should - * have been sent from my own socket and has no rcvif in this case. - * It is also necessary because someone might consider it as - * 'ifnet *', and cause SEGV. - */ #if defined(IPFIREWALL) && defined(DUMMYNET) /* * dummynet packet are prepended a vestigial mbuf with * m_type = MT_DUMMYNET and m_data pointing to the matching * rule. */ if (m->m_type == MT_DUMMYNET) { /* * the packet was already tagged, so part of the * processing was already done, and we need to go down. * Get parameters from the header. */ rule = (struct ip_fw_chain *)(m->m_data) ; opt = NULL ; ro = & ( ((struct dn_pkt *)m)->ro ) ; imo = NULL ; dst = ((struct dn_pkt *)m)->dn_dst ; ifp = ((struct dn_pkt *)m)->ifp ; flags = ((struct dn_pkt *)m)->flags ; m0 = m = m->m_next ; #ifdef IPSEC - if ((flags & IP_SOCKINMRCVIF) != 0) { - so = (struct socket *)m->m_pkthdr.rcvif; - m->m_pkthdr.rcvif = NULL; - } + so = ipsec_getsocket(m); + ipsec_setsocket(m, NULL); #endif ip = mtod(m, struct ip *); hlen = IP_VHL_HL(ip->ip_vhl) << 2 ; goto sendit; } else rule = NULL ; #endif #ifdef IPSEC - if ((flags & IP_SOCKINMRCVIF) != 0) { - so = (struct socket *)m->m_pkthdr.rcvif; - m->m_pkthdr.rcvif = NULL; - } + so = ipsec_getsocket(m); + ipsec_setsocket(m, NULL); #endif #ifdef DIAGNOSTIC if ((m->m_flags & M_PKTHDR) == 0) panic("ip_output no HDR"); if (!ro) panic("ip_output no route, proto = %d", mtod(m, struct ip *)->ip_p); #endif if (opt) { m = ip_insertoptions(m, opt, &len); hlen = len; } ip = mtod(m, struct ip *); /* * Fill in IP header. */ if ((flags & (IP_FORWARDING|IP_RAWOUTPUT)) == 0) { ip->ip_vhl = IP_MAKE_VHL(IPVERSION, hlen >> 2); ip->ip_off &= IP_DF; ip->ip_id = htons(ip_id++); ipstat.ips_localout++; } else { hlen = IP_VHL_HL(ip->ip_vhl) << 2; } dst = (struct sockaddr_in *)&ro->ro_dst; /* * If there is a cached route, * check that it is to the same destination * and is still up. If not, free it and try again. */ if (ro->ro_rt && ((ro->ro_rt->rt_flags & RTF_UP) == 0 || dst->sin_addr.s_addr != ip->ip_dst.s_addr)) { RTFREE(ro->ro_rt); ro->ro_rt = (struct rtentry *)0; } if (ro->ro_rt == 0) { dst->sin_family = AF_INET; dst->sin_len = sizeof(*dst); dst->sin_addr = ip->ip_dst; } /* * If routing to interface only, * short circuit routing lookup. */ #define ifatoia(ifa) ((struct in_ifaddr *)(ifa)) #define sintosa(sin) ((struct sockaddr *)(sin)) if (flags & IP_ROUTETOIF) { if ((ia = ifatoia(ifa_ifwithdstaddr(sintosa(dst)))) == 0 && (ia = ifatoia(ifa_ifwithnet(sintosa(dst)))) == 0) { ipstat.ips_noroute++; error = ENETUNREACH; goto bad; } ifp = ia->ia_ifp; ip->ip_ttl = 1; isbroadcast = in_broadcast(dst->sin_addr, ifp); } else { /* * If this is the case, we probably don't want to allocate * a protocol-cloned route since we didn't get one from the * ULP. This lets TCP do its thing, while not burdening * forwarding or ICMP with the overhead of cloning a route. * Of course, we still want to do any cloning requested by * the link layer, as this is probably required in all cases * for correct operation (as it is for ARP). */ if (ro->ro_rt == 0) rtalloc_ign(ro, RTF_PRCLONING); if (ro->ro_rt == 0) { ipstat.ips_noroute++; error = EHOSTUNREACH; goto bad; } ia = ifatoia(ro->ro_rt->rt_ifa); ifp = ro->ro_rt->rt_ifp; ro->ro_rt->rt_use++; if (ro->ro_rt->rt_flags & RTF_GATEWAY) dst = (struct sockaddr_in *)ro->ro_rt->rt_gateway; if (ro->ro_rt->rt_flags & RTF_HOST) isbroadcast = (ro->ro_rt->rt_flags & RTF_BROADCAST); else isbroadcast = in_broadcast(dst->sin_addr, ifp); } if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr))) { struct in_multi *inm; m->m_flags |= M_MCAST; /* * IP destination address is multicast. Make sure "dst" * still points to the address in "ro". (It may have been * changed to point to a gateway address, above.) */ dst = (struct sockaddr_in *)&ro->ro_dst; /* * See if the caller provided any multicast options */ if (imo != NULL) { ip->ip_ttl = imo->imo_multicast_ttl; if (imo->imo_multicast_ifp != NULL) ifp = imo->imo_multicast_ifp; if (imo->imo_multicast_vif != -1) ip->ip_src.s_addr = ip_mcast_src(imo->imo_multicast_vif); } else ip->ip_ttl = IP_DEFAULT_MULTICAST_TTL; /* * Confirm that the outgoing interface supports multicast. */ if ((imo == NULL) || (imo->imo_multicast_vif == -1)) { if ((ifp->if_flags & IFF_MULTICAST) == 0) { ipstat.ips_noroute++; error = ENETUNREACH; goto bad; } } /* * If source address not specified yet, use address * of outgoing interface. */ if (ip->ip_src.s_addr == INADDR_ANY) { register struct in_ifaddr *ia1; for (ia1 = in_ifaddrhead.tqh_first; ia1; ia1 = ia1->ia_link.tqe_next) if (ia1->ia_ifp == ifp) { ip->ip_src = IA_SIN(ia1)->sin_addr; break; } } IN_LOOKUP_MULTI(ip->ip_dst, ifp, inm); if (inm != NULL && (imo == NULL || imo->imo_multicast_loop)) { /* * If we belong to the destination multicast group * on the outgoing interface, and the caller did not * forbid loopback, loop back a copy. */ ip_mloopback(ifp, m, dst, hlen); } else { /* * If we are acting as a multicast router, perform * multicast forwarding as if the packet had just * arrived on the interface to which we are about * to send. The multicast forwarding function * recursively calls this function, using the * IP_FORWARDING flag to prevent infinite recursion. * * Multicasts that are looped back by ip_mloopback(), * above, will be forwarded by the ip_input() routine, * if necessary. */ if (ip_mrouter && (flags & IP_FORWARDING) == 0) { /* * Check if rsvp daemon is running. If not, don't * set ip_moptions. This ensures that the packet * is multicast and not just sent down one link * as prescribed by rsvpd. */ if (!rsvp_on) imo = NULL; if (ip_mforward(ip, ifp, m, imo) != 0) { m_freem(m); goto done; } } } /* * Multicasts with a time-to-live of zero may be looped- * back, above, but must not be transmitted on a network. * Also, multicasts addressed to the loopback interface * are not sent -- the above call to ip_mloopback() will * loop back a copy if this host actually belongs to the * destination group on the loopback interface. */ if (ip->ip_ttl == 0 || ifp->if_flags & IFF_LOOPBACK) { m_freem(m); goto done; } goto sendit; } #ifndef notdef /* * If source address not specified yet, use address * of outgoing interface. */ if (ip->ip_src.s_addr == INADDR_ANY) { ip->ip_src = IA_SIN(ia)->sin_addr; #ifdef IPFIREWALL_FORWARD /* Keep note that we did this - if the firewall changes * the next-hop, our interface may change, changing the * default source IP. It's a shame so much effort happens * twice. Oh well. */ fwd_rewrite_src++; #endif /* IPFIREWALL_FORWARD */ } #endif /* notdef */ /* * Verify that we have any chance at all of being able to queue * the packet or packet fragments */ if ((ifp->if_snd.ifq_len + ip->ip_len / ifp->if_mtu + 1) >= ifp->if_snd.ifq_maxlen) { error = ENOBUFS; goto bad; } /* * Look for broadcast address and * and verify user is allowed to send * such a packet. */ if (isbroadcast) { if ((ifp->if_flags & IFF_BROADCAST) == 0) { error = EADDRNOTAVAIL; goto bad; } if ((flags & IP_ALLOWBROADCAST) == 0) { error = EACCES; goto bad; } /* don't allow broadcast messages to be fragmented */ if ((u_short)ip->ip_len > ifp->if_mtu) { error = EMSGSIZE; goto bad; } m->m_flags |= M_BCAST; } else { m->m_flags &= ~M_BCAST; } sendit: /* * IpHack's section. * - Xlate: translate packet's addr/port (NAT). * - Firewall: deny/allow/etc. * - Wrap: fake packet's addr/port * - Encapsulate: put it in another IP and send out. */ if (fr_checkp) { struct mbuf *m1 = m; if ((error = (*fr_checkp)(ip, hlen, ifp, 1, &m1)) || !m1) goto done; ip = mtod(m = m1, struct ip *); } /* * Check with the firewall... */ if (fw_enable && ip_fw_chk_ptr) { struct sockaddr_in *old = dst; off = (*ip_fw_chk_ptr)(&ip, hlen, ifp, &divert_cookie, &m, &rule, &dst); /* * On return we must do the following: * m == NULL -> drop the pkt * 1<=off<= 0xffff -> DIVERT * (off & 0x10000) -> send to a DUMMYNET pipe * (off & 0x20000) -> TEE the packet * dst != old -> IPFIREWALL_FORWARD * off==0, dst==old -> accept * If some of the above modules is not compiled in, then * we should't have to check the corresponding condition * (because the ipfw control socket should not accept * unsupported rules), but better play safe and drop * packets in case of doubt. */ if (!m) { /* firewall said to reject */ error = EACCES; goto done; } if (off == 0 && dst == old) /* common case */ goto pass ; #ifdef DUMMYNET if ((off & IP_FW_PORT_DYNT_FLAG) != 0) { /* * pass the pkt to dummynet. Need to include * pipe number, m, ifp, ro, dst because these are * not recomputed in the next pass. * All other parameters have been already used and * so they are not needed anymore. * XXX note: if the ifp or ro entry are deleted * while a pkt is in dummynet, we are in trouble! */ dummynet_io(off & 0xffff, DN_TO_IP_OUT, m,ifp,ro,dst,rule, flags); goto done; } #endif #ifdef IPDIVERT if (off != 0 && (off & IP_FW_PORT_DYNT_FLAG) == 0) { struct mbuf *clone = NULL; /* Clone packet if we're doing a 'tee' */ if ((off & IP_FW_PORT_TEE_FLAG) != 0) clone = m_dup(m, M_DONTWAIT); /* * XXX * delayed checksums are not currently compatible * with divert sockets. */ if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) { in_delayed_cksum(m); m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA; } /* Restore packet header fields to original values */ HTONS(ip->ip_len); HTONS(ip->ip_off); /* Deliver packet to divert input routine */ ip_divert_cookie = divert_cookie; divert_packet(m, 0, off & 0xffff); /* If 'tee', continue with original packet */ if (clone != NULL) { m = clone; ip = mtod(m, struct ip *); goto pass; } goto done; } #endif #ifdef IPFIREWALL_FORWARD /* Here we check dst to make sure it's directly reachable on the * interface we previously thought it was. * If it isn't (which may be likely in some situations) we have * to re-route it (ie, find a route for the next-hop and the * associated interface) and set them here. This is nested * forwarding which in most cases is undesirable, except where * such control is nigh impossible. So we do it here. * And I'm babbling. */ if (off == 0 && old != dst) { struct in_ifaddr *ia; /* It's changed... */ /* There must be a better way to do this next line... */ static struct route sro_fwd, *ro_fwd = &sro_fwd; #ifdef IPFIREWALL_FORWARD_DEBUG printf("IPFIREWALL_FORWARD: New dst ip: "); print_ip(dst->sin_addr); printf("\n"); #endif /* * We need to figure out if we have been forwarded * to a local socket. If so then we should somehow * "loop back" to ip_input, and get directed to the * PCB as if we had received this packet. This is * because it may be dificult to identify the packets * you want to forward until they are being output * and have selected an interface. (e.g. locally * initiated packets) If we used the loopback inteface, * we would not be able to control what happens * as the packet runs through ip_input() as * it is done through a ISR. */ for (ia = TAILQ_FIRST(&in_ifaddrhead); ia; ia = TAILQ_NEXT(ia, ia_link)) { /* * If the addr to forward to is one * of ours, we pretend to * be the destination for this packet. */ if (IA_SIN(ia)->sin_addr.s_addr == dst->sin_addr.s_addr) break; } if (ia) { /* tell ip_input "dont filter" */ ip_fw_fwd_addr = dst; if (m->m_pkthdr.rcvif == NULL) m->m_pkthdr.rcvif = ifunit("lo0"); if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) { m->m_pkthdr.csum_flags |= CSUM_DATA_VALID | CSUM_PSEUDO_HDR; m0->m_pkthdr.csum_data = 0xffff; } m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED | CSUM_IP_VALID; ip->ip_len = htons((u_short)ip->ip_len); ip->ip_off = htons((u_short)ip->ip_off); ip_input(m); goto done; } /* Some of the logic for this was * nicked from above. * * This rewrites the cached route in a local PCB. * Is this what we want to do? */ bcopy(dst, &ro_fwd->ro_dst, sizeof(*dst)); ro_fwd->ro_rt = 0; rtalloc_ign(ro_fwd, RTF_PRCLONING); if (ro_fwd->ro_rt == 0) { ipstat.ips_noroute++; error = EHOSTUNREACH; goto bad; } ia = ifatoia(ro_fwd->ro_rt->rt_ifa); ifp = ro_fwd->ro_rt->rt_ifp; ro_fwd->ro_rt->rt_use++; if (ro_fwd->ro_rt->rt_flags & RTF_GATEWAY) dst = (struct sockaddr_in *)ro_fwd->ro_rt->rt_gateway; if (ro_fwd->ro_rt->rt_flags & RTF_HOST) isbroadcast = (ro_fwd->ro_rt->rt_flags & RTF_BROADCAST); else isbroadcast = in_broadcast(dst->sin_addr, ifp); RTFREE(ro->ro_rt); ro->ro_rt = ro_fwd->ro_rt; dst = (struct sockaddr_in *)&ro_fwd->ro_dst; /* * If we added a default src ip earlier, * which would have been gotten from the-then * interface, do it again, from the new one. */ if (fwd_rewrite_src) ip->ip_src = IA_SIN(ia)->sin_addr; goto pass ; } #endif /* IPFIREWALL_FORWARD */ /* * if we get here, none of the above matches, and * we have to drop the pkt */ m_freem(m); error = EACCES; /* not sure this is the right error msg */ goto done; } pass: #ifdef IPSEC /* get SP for this packet */ if (so == NULL) sp = ipsec4_getpolicybyaddr(m, IPSEC_DIR_OUTBOUND, flags, &error); else sp = ipsec4_getpolicybysock(m, IPSEC_DIR_OUTBOUND, so, &error); if (sp == NULL) { ipsecstat.out_inval++; goto bad; } error = 0; /* check policy */ switch (sp->policy) { case IPSEC_POLICY_DISCARD: /* * This packet is just discarded. */ ipsecstat.out_polvio++; goto bad; case IPSEC_POLICY_BYPASS: case IPSEC_POLICY_NONE: /* no need to do IPsec. */ goto skip_ipsec; case IPSEC_POLICY_IPSEC: if (sp->req == NULL) { /* XXX should be panic ? */ printf("ip_output: No IPsec request specified.\n"); error = EINVAL; goto bad; } break; case IPSEC_POLICY_ENTRUST: default: printf("ip_output: Invalid policy found. %d\n", sp->policy); } { struct ipsec_output_state state; bzero(&state, sizeof(state)); state.m = m; if (flags & IP_ROUTETOIF) { state.ro = &iproute; bzero(&iproute, sizeof(iproute)); } else state.ro = ro; state.dst = (struct sockaddr *)dst; ip->ip_sum = 0; /* * XXX * delayed checksums are not currently compatible with IPsec */ if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) { in_delayed_cksum(m); m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA; } ip->ip_len = htons((u_short)ip->ip_len); ip->ip_off = htons((u_short)ip->ip_off); error = ipsec4_output(&state, sp, flags); m = state.m; if (flags & IP_ROUTETOIF) { /* * if we have tunnel mode SA, we may need to ignore * IP_ROUTETOIF. */ if (state.ro != &iproute || state.ro->ro_rt != NULL) { flags &= ~IP_ROUTETOIF; ro = state.ro; } } else ro = state.ro; dst = (struct sockaddr_in *)state.dst; if (error) { /* mbuf is already reclaimed in ipsec4_output. */ m0 = NULL; switch (error) { case EHOSTUNREACH: case ENETUNREACH: case EMSGSIZE: case ENOBUFS: case ENOMEM: break; default: printf("ip4_output (ipsec): error code %d\n", error); /*fall through*/ case ENOENT: /* don't show these error codes to the user */ error = 0; break; } goto bad; } } /* be sure to update variables that are affected by ipsec4_output() */ ip = mtod(m, struct ip *); #ifdef _IP_VHL hlen = IP_VHL_HL(ip->ip_vhl) << 2; #else hlen = ip->ip_hl << 2; #endif if (ro->ro_rt == NULL) { if ((flags & IP_ROUTETOIF) == 0) { printf("ip_output: " "can't update route after IPsec processing\n"); error = EHOSTUNREACH; /*XXX*/ goto bad; } } else { /* nobody uses ia beyond here */ ifp = ro->ro_rt->rt_ifp; } /* make it flipped, again. */ ip->ip_len = ntohs((u_short)ip->ip_len); ip->ip_off = ntohs((u_short)ip->ip_off); skip_ipsec: #endif /*IPSEC*/ sw_csum = m->m_pkthdr.csum_flags | CSUM_IP; m->m_pkthdr.csum_flags = sw_csum & ifp->if_hwassist; sw_csum &= ~ifp->if_hwassist; if (sw_csum & CSUM_DELAY_DATA) { in_delayed_cksum(m); sw_csum &= ~CSUM_DELAY_DATA; } /* * If small enough for interface, or the interface will take * care of the fragmentation for us, can just send directly. */ if ((u_short)ip->ip_len <= ifp->if_mtu || ifp->if_hwassist & CSUM_FRAGMENT) { ip->ip_len = htons((u_short)ip->ip_len); ip->ip_off = htons((u_short)ip->ip_off); ip->ip_sum = 0; if (sw_csum & CSUM_DELAY_IP) { if (ip->ip_vhl == IP_VHL_BORING) { ip->ip_sum = in_cksum_hdr(ip); } else { ip->ip_sum = in_cksum(m, hlen); } } error = (*ifp->if_output)(ifp, m, (struct sockaddr *)dst, ro->ro_rt); goto done; } /* * Too large for interface; fragment if possible. * Must be able to put at least 8 bytes per fragment. */ if (ip->ip_off & IP_DF) { error = EMSGSIZE; /* * This case can happen if the user changed the MTU * of an interface after enabling IP on it. Because * most netifs don't keep track of routes pointing to * them, there is no way for one to update all its * routes when the MTU is changed. */ if ((ro->ro_rt->rt_flags & (RTF_UP | RTF_HOST)) && !(ro->ro_rt->rt_rmx.rmx_locks & RTV_MTU) && (ro->ro_rt->rt_rmx.rmx_mtu > ifp->if_mtu)) { ro->ro_rt->rt_rmx.rmx_mtu = ifp->if_mtu; } ipstat.ips_cantfrag++; goto bad; } len = (ifp->if_mtu - hlen) &~ 7; if (len < 8) { error = EMSGSIZE; goto bad; } /* * if the interface will not calculate checksums on * fragmented packets, then do it here. */ if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA && (ifp->if_hwassist & CSUM_IP_FRAGS) == 0) { in_delayed_cksum(m); m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA; } { int mhlen, firstlen = len; struct mbuf **mnext = &m->m_nextpkt; int nfrags = 1; /* * Loop through length of segment after first fragment, * make new header and copy data of each part and link onto chain. */ m0 = m; mhlen = sizeof (struct ip); for (off = hlen + len; off < (u_short)ip->ip_len; off += len) { MGETHDR(m, M_DONTWAIT, MT_HEADER); if (m == 0) { error = ENOBUFS; ipstat.ips_odropped++; goto sendorfree; } m->m_flags |= (m0->m_flags & M_MCAST) | M_FRAG; m->m_data += max_linkhdr; mhip = mtod(m, struct ip *); *mhip = *ip; if (hlen > sizeof (struct ip)) { mhlen = ip_optcopy(ip, mhip) + sizeof (struct ip); mhip->ip_vhl = IP_MAKE_VHL(IPVERSION, mhlen >> 2); } m->m_len = mhlen; mhip->ip_off = ((off - hlen) >> 3) + (ip->ip_off & ~IP_MF); if (ip->ip_off & IP_MF) mhip->ip_off |= IP_MF; if (off + len >= (u_short)ip->ip_len) len = (u_short)ip->ip_len - off; else mhip->ip_off |= IP_MF; mhip->ip_len = htons((u_short)(len + mhlen)); m->m_next = m_copy(m0, off, len); if (m->m_next == 0) { (void) m_free(m); error = ENOBUFS; /* ??? */ ipstat.ips_odropped++; goto sendorfree; } m->m_pkthdr.len = mhlen + len; m->m_pkthdr.rcvif = (struct ifnet *)0; m->m_pkthdr.csum_flags = m0->m_pkthdr.csum_flags; mhip->ip_off = htons((u_short)mhip->ip_off); mhip->ip_sum = 0; if (sw_csum & CSUM_DELAY_IP) { if (mhip->ip_vhl == IP_VHL_BORING) { mhip->ip_sum = in_cksum_hdr(mhip); } else { mhip->ip_sum = in_cksum(m, mhlen); } } *mnext = m; mnext = &m->m_nextpkt; nfrags++; } ipstat.ips_ofragments += nfrags; /* set first/last markers for fragment chain */ m->m_flags |= M_LASTFRAG; m0->m_flags |= M_FIRSTFRAG | M_FRAG; m0->m_pkthdr.csum_data = nfrags; /* * Update first fragment by trimming what's been copied out * and updating header, then send each fragment (in order). */ m = m0; m_adj(m, hlen + firstlen - (u_short)ip->ip_len); m->m_pkthdr.len = hlen + firstlen; ip->ip_len = htons((u_short)m->m_pkthdr.len); ip->ip_off = htons((u_short)(ip->ip_off | IP_MF)); ip->ip_sum = 0; if (sw_csum & CSUM_DELAY_IP) { if (ip->ip_vhl == IP_VHL_BORING) { ip->ip_sum = in_cksum_hdr(ip); } else { ip->ip_sum = in_cksum(m, hlen); } } sendorfree: for (m = m0; m; m = m0) { m0 = m->m_nextpkt; m->m_nextpkt = 0; if (error == 0) error = (*ifp->if_output)(ifp, m, (struct sockaddr *)dst, ro->ro_rt); else m_freem(m); } if (error == 0) ipstat.ips_fragmented++; } done: #ifdef IPSEC if (ro == &iproute && ro->ro_rt) { RTFREE(ro->ro_rt); ro->ro_rt = NULL; } if (sp != NULL) { KEYDEBUG(KEYDEBUG_IPSEC_STAMP, printf("DP ip_output call free SP:%p\n", sp)); key_freesp(sp); } #endif /* IPSEC */ return (error); bad: m_freem(m0); goto done; } void in_delayed_cksum(struct mbuf *m) { struct ip *ip; u_short csum, offset; ip = mtod(m, struct ip *); offset = IP_VHL_HL(ip->ip_vhl) << 2 ; csum = in_cksum_skip(m, ip->ip_len, offset); offset += m->m_pkthdr.csum_data; /* checksum offset */ if (offset + sizeof(u_short) > m->m_len) { printf("delayed m_pullup, m->len: %d off: %d p: %d\n", m->m_len, offset, ip->ip_p); /* * XXX * this shouldn't happen, but if it does, the * correct behavior may be to insert the checksum * in the existing chain instead of rearranging it. */ m = m_pullup(m, offset + sizeof(u_short)); } *(u_short *)(m->m_data + offset) = csum; } /* * Insert IP options into preformed packet. * Adjust IP destination as required for IP source routing, * as indicated by a non-zero in_addr at the start of the options. * * XXX This routine assumes that the packet has no options in place. */ static struct mbuf * ip_insertoptions(m, opt, phlen) register struct mbuf *m; struct mbuf *opt; int *phlen; { register struct ipoption *p = mtod(opt, struct ipoption *); struct mbuf *n; register struct ip *ip = mtod(m, struct ip *); unsigned optlen; optlen = opt->m_len - sizeof(p->ipopt_dst); if (optlen + (u_short)ip->ip_len > IP_MAXPACKET) return (m); /* XXX should fail */ if (p->ipopt_dst.s_addr) ip->ip_dst = p->ipopt_dst; if (m->m_flags & M_EXT || m->m_data - optlen < m->m_pktdat) { MGETHDR(n, M_DONTWAIT, MT_HEADER); if (n == 0) return (m); n->m_pkthdr.rcvif = (struct ifnet *)0; n->m_pkthdr.len = m->m_pkthdr.len + optlen; m->m_len -= sizeof(struct ip); m->m_data += sizeof(struct ip); n->m_next = m; m = n; m->m_len = optlen + sizeof(struct ip); m->m_data += max_linkhdr; (void)memcpy(mtod(m, void *), ip, sizeof(struct ip)); } else { m->m_data -= optlen; m->m_len += optlen; m->m_pkthdr.len += optlen; ovbcopy((caddr_t)ip, mtod(m, caddr_t), sizeof(struct ip)); } ip = mtod(m, struct ip *); bcopy(p->ipopt_list, ip + 1, optlen); *phlen = sizeof(struct ip) + optlen; ip->ip_vhl = IP_MAKE_VHL(IPVERSION, *phlen >> 2); ip->ip_len += optlen; return (m); } /* * Copy options from ip to jp, * omitting those not copied during fragmentation. */ int ip_optcopy(ip, jp) struct ip *ip, *jp; { register u_char *cp, *dp; int opt, optlen, cnt; cp = (u_char *)(ip + 1); dp = (u_char *)(jp + 1); cnt = (IP_VHL_HL(ip->ip_vhl) << 2) - sizeof (struct ip); for (; cnt > 0; cnt -= optlen, cp += optlen) { opt = cp[0]; if (opt == IPOPT_EOL) break; if (opt == IPOPT_NOP) { /* Preserve for IP mcast tunnel's LSRR alignment. */ *dp++ = IPOPT_NOP; optlen = 1; continue; - } else - optlen = cp[IPOPT_OLEN]; + } +#ifdef DIAGNOSTIC + if (cnt < IPOPT_OLEN + sizeof(*cp)) + panic("malformed IPv4 option passed to ip_optcopy"); +#endif + optlen = cp[IPOPT_OLEN]; +#ifdef DIAGNOSTIC + if (optlen < IPOPT_OLEN + sizeof(*cp) || optlen > cnt) + panic("malformed IPv4 option passed to ip_optcopy"); +#endif /* bogus lengths should have been caught by ip_dooptions */ if (optlen > cnt) optlen = cnt; if (IPOPT_COPIED(opt)) { bcopy(cp, dp, optlen); dp += optlen; } } for (optlen = dp - (u_char *)(jp+1); optlen & 0x3; optlen++) *dp++ = IPOPT_EOL; return (optlen); } /* * IP socket option processing. */ int ip_ctloutput(so, sopt) struct socket *so; struct sockopt *sopt; { struct inpcb *inp = sotoinpcb(so); int error, optval; error = optval = 0; if (sopt->sopt_level != IPPROTO_IP) { return (EINVAL); } switch (sopt->sopt_dir) { case SOPT_SET: switch (sopt->sopt_name) { case IP_OPTIONS: #ifdef notyet case IP_RETOPTS: #endif { struct mbuf *m; if (sopt->sopt_valsize > MLEN) { error = EMSGSIZE; break; } MGET(m, sopt->sopt_p ? M_WAIT : M_DONTWAIT, MT_HEADER); if (m == 0) { error = ENOBUFS; break; } m->m_len = sopt->sopt_valsize; error = sooptcopyin(sopt, mtod(m, char *), m->m_len, m->m_len); return (ip_pcbopts(sopt->sopt_name, &inp->inp_options, m)); } case IP_TOS: case IP_TTL: case IP_RECVOPTS: case IP_RECVRETOPTS: case IP_RECVDSTADDR: case IP_RECVIF: #if defined(NFAITH) && NFAITH > 0 case IP_FAITH: #endif error = sooptcopyin(sopt, &optval, sizeof optval, sizeof optval); if (error) break; switch (sopt->sopt_name) { case IP_TOS: inp->inp_ip_tos = optval; break; case IP_TTL: inp->inp_ip_ttl = optval; break; #define OPTSET(bit) \ if (optval) \ inp->inp_flags |= bit; \ else \ inp->inp_flags &= ~bit; case IP_RECVOPTS: OPTSET(INP_RECVOPTS); break; case IP_RECVRETOPTS: OPTSET(INP_RECVRETOPTS); break; case IP_RECVDSTADDR: OPTSET(INP_RECVDSTADDR); break; case IP_RECVIF: OPTSET(INP_RECVIF); break; #if defined(NFAITH) && NFAITH > 0 case IP_FAITH: OPTSET(INP_FAITH); break; #endif } break; #undef OPTSET case IP_MULTICAST_IF: case IP_MULTICAST_VIF: case IP_MULTICAST_TTL: case IP_MULTICAST_LOOP: case IP_ADD_MEMBERSHIP: case IP_DROP_MEMBERSHIP: error = ip_setmoptions(sopt, &inp->inp_moptions); break; case IP_PORTRANGE: error = sooptcopyin(sopt, &optval, sizeof optval, sizeof optval); if (error) break; switch (optval) { case IP_PORTRANGE_DEFAULT: inp->inp_flags &= ~(INP_LOWPORT); inp->inp_flags &= ~(INP_HIGHPORT); break; case IP_PORTRANGE_HIGH: inp->inp_flags &= ~(INP_LOWPORT); inp->inp_flags |= INP_HIGHPORT; break; case IP_PORTRANGE_LOW: inp->inp_flags &= ~(INP_HIGHPORT); inp->inp_flags |= INP_LOWPORT; break; default: error = EINVAL; break; } break; #ifdef IPSEC case IP_IPSEC_POLICY: { caddr_t req; + size_t len = 0; int priv; struct mbuf *m; int optname; if ((error = soopt_getm(sopt, &m)) != 0) /* XXX */ break; if ((error = soopt_mcopyin(sopt, m)) != 0) /* XXX */ break; priv = (sopt->sopt_p != NULL && suser(sopt->sopt_p) != 0) ? 0 : 1; req = mtod(m, caddr_t); + len = m->m_len; optname = sopt->sopt_name; - error = ipsec4_set_policy(inp, optname, req, priv); + error = ipsec4_set_policy(inp, optname, req, len, priv); m_freem(m); break; } #endif /*IPSEC*/ default: error = ENOPROTOOPT; break; } break; case SOPT_GET: switch (sopt->sopt_name) { case IP_OPTIONS: case IP_RETOPTS: if (inp->inp_options) error = sooptcopyout(sopt, mtod(inp->inp_options, char *), inp->inp_options->m_len); else sopt->sopt_valsize = 0; break; case IP_TOS: case IP_TTL: case IP_RECVOPTS: case IP_RECVRETOPTS: case IP_RECVDSTADDR: case IP_RECVIF: case IP_PORTRANGE: #if defined(NFAITH) && NFAITH > 0 case IP_FAITH: #endif switch (sopt->sopt_name) { case IP_TOS: optval = inp->inp_ip_tos; break; case IP_TTL: optval = inp->inp_ip_ttl; break; #define OPTBIT(bit) (inp->inp_flags & bit ? 1 : 0) case IP_RECVOPTS: optval = OPTBIT(INP_RECVOPTS); break; case IP_RECVRETOPTS: optval = OPTBIT(INP_RECVRETOPTS); break; case IP_RECVDSTADDR: optval = OPTBIT(INP_RECVDSTADDR); break; case IP_RECVIF: optval = OPTBIT(INP_RECVIF); break; case IP_PORTRANGE: if (inp->inp_flags & INP_HIGHPORT) optval = IP_PORTRANGE_HIGH; else if (inp->inp_flags & INP_LOWPORT) optval = IP_PORTRANGE_LOW; else optval = 0; break; #if defined(NFAITH) && NFAITH > 0 case IP_FAITH: optval = OPTBIT(INP_FAITH); break; #endif } error = sooptcopyout(sopt, &optval, sizeof optval); break; case IP_MULTICAST_IF: case IP_MULTICAST_VIF: case IP_MULTICAST_TTL: case IP_MULTICAST_LOOP: case IP_ADD_MEMBERSHIP: case IP_DROP_MEMBERSHIP: error = ip_getmoptions(sopt, inp->inp_moptions); break; #ifdef IPSEC case IP_IPSEC_POLICY: { struct mbuf *m = NULL; caddr_t req = NULL; + size_t len = 0; - if (m != 0) + if (m != 0) { req = mtod(m, caddr_t); - error = ipsec4_get_policy(sotoinpcb(so), req, &m); + len = m->m_len; + } + error = ipsec4_get_policy(sotoinpcb(so), req, len, &m); if (error == 0) error = soopt_mcopyout(sopt, m); /* XXX */ if (error == 0) m_freem(m); break; } #endif /*IPSEC*/ default: error = ENOPROTOOPT; break; } break; } return (error); } /* * Set up IP options in pcb for insertion in output packets. * Store in mbuf with pointer in pcbopt, adding pseudo-option * with destination address if source routed. */ static int ip_pcbopts(optname, pcbopt, m) int optname; struct mbuf **pcbopt; register struct mbuf *m; { register int cnt, optlen; register u_char *cp; u_char opt; /* turn off any old options */ if (*pcbopt) (void)m_free(*pcbopt); *pcbopt = 0; if (m == (struct mbuf *)0 || m->m_len == 0) { /* * Only turning off any previous options. */ if (m) (void)m_free(m); return (0); } #ifndef vax if (m->m_len % sizeof(int32_t)) goto bad; #endif /* * IP first-hop destination address will be stored before * actual options; move other options back * and clear it when none present. */ if (m->m_data + m->m_len + sizeof(struct in_addr) >= &m->m_dat[MLEN]) goto bad; cnt = m->m_len; m->m_len += sizeof(struct in_addr); cp = mtod(m, u_char *) + sizeof(struct in_addr); ovbcopy(mtod(m, caddr_t), (caddr_t)cp, (unsigned)cnt); bzero(mtod(m, caddr_t), sizeof(struct in_addr)); for (; cnt > 0; cnt -= optlen, cp += optlen) { opt = cp[IPOPT_OPTVAL]; if (opt == IPOPT_EOL) break; if (opt == IPOPT_NOP) optlen = 1; else { if (cnt < IPOPT_OLEN + sizeof(*cp)) goto bad; optlen = cp[IPOPT_OLEN]; if (optlen < IPOPT_OLEN + sizeof(*cp) || optlen > cnt) goto bad; } switch (opt) { default: break; case IPOPT_LSRR: case IPOPT_SSRR: /* * user process specifies route as: * ->A->B->C->D * D must be our final destination (but we can't * check that since we may not have connected yet). * A is first hop destination, which doesn't appear in * actual IP option, but is stored before the options. */ if (optlen < IPOPT_MINOFF - 1 + sizeof(struct in_addr)) goto bad; m->m_len -= sizeof(struct in_addr); cnt -= sizeof(struct in_addr); optlen -= sizeof(struct in_addr); cp[IPOPT_OLEN] = optlen; /* * Move first hop before start of options. */ bcopy((caddr_t)&cp[IPOPT_OFFSET+1], mtod(m, caddr_t), sizeof(struct in_addr)); /* * Then copy rest of options back * to close up the deleted entry. */ ovbcopy((caddr_t)(&cp[IPOPT_OFFSET+1] + sizeof(struct in_addr)), (caddr_t)&cp[IPOPT_OFFSET+1], (unsigned)cnt + sizeof(struct in_addr)); break; } } if (m->m_len > MAX_IPOPTLEN + sizeof(struct in_addr)) goto bad; *pcbopt = m; return (0); bad: (void)m_free(m); return (EINVAL); } /* * XXX * The whole multicast option thing needs to be re-thought. * Several of these options are equally applicable to non-multicast * transmission, and one (IP_MULTICAST_TTL) totally duplicates a * standard option (IP_TTL). */ /* * Set the IP multicast options in response to user setsockopt(). */ static int ip_setmoptions(sopt, imop) struct sockopt *sopt; struct ip_moptions **imop; { int error = 0; int i; struct in_addr addr; struct ip_mreq mreq; struct ifnet *ifp; struct ip_moptions *imo = *imop; struct route ro; struct sockaddr_in *dst; int s; if (imo == NULL) { /* * No multicast option buffer attached to the pcb; * allocate one and initialize to default values. */ imo = (struct ip_moptions*)malloc(sizeof(*imo), M_IPMOPTS, M_WAITOK); if (imo == NULL) return (ENOBUFS); *imop = imo; imo->imo_multicast_ifp = NULL; imo->imo_multicast_vif = -1; imo->imo_multicast_ttl = IP_DEFAULT_MULTICAST_TTL; imo->imo_multicast_loop = IP_DEFAULT_MULTICAST_LOOP; imo->imo_num_memberships = 0; } switch (sopt->sopt_name) { /* store an index number for the vif you wanna use in the send */ case IP_MULTICAST_VIF: if (legal_vif_num == 0) { error = EOPNOTSUPP; break; } error = sooptcopyin(sopt, &i, sizeof i, sizeof i); if (error) break; if (!legal_vif_num(i) && (i != -1)) { error = EINVAL; break; } imo->imo_multicast_vif = i; break; case IP_MULTICAST_IF: /* * Select the interface for outgoing multicast packets. */ error = sooptcopyin(sopt, &addr, sizeof addr, sizeof addr); if (error) break; /* * INADDR_ANY is used to remove a previous selection. * When no interface is selected, a default one is * chosen every time a multicast packet is sent. */ if (addr.s_addr == INADDR_ANY) { imo->imo_multicast_ifp = NULL; break; } /* * The selected interface is identified by its local * IP address. Find the interface and confirm that * it supports multicasting. */ s = splimp(); INADDR_TO_IFP(addr, ifp); if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) { splx(s); error = EADDRNOTAVAIL; break; } imo->imo_multicast_ifp = ifp; splx(s); break; case IP_MULTICAST_TTL: /* * Set the IP time-to-live for outgoing multicast packets. * The original multicast API required a char argument, * which is inconsistent with the rest of the socket API. * We allow either a char or an int. */ if (sopt->sopt_valsize == 1) { u_char ttl; error = sooptcopyin(sopt, &ttl, 1, 1); if (error) break; imo->imo_multicast_ttl = ttl; } else { u_int ttl; error = sooptcopyin(sopt, &ttl, sizeof ttl, sizeof ttl); if (error) break; if (ttl > 255) error = EINVAL; else imo->imo_multicast_ttl = ttl; } break; case IP_MULTICAST_LOOP: /* * Set the loopback flag for outgoing multicast packets. * Must be zero or one. The original multicast API required a * char argument, which is inconsistent with the rest * of the socket API. We allow either a char or an int. */ if (sopt->sopt_valsize == 1) { u_char loop; error = sooptcopyin(sopt, &loop, 1, 1); if (error) break; imo->imo_multicast_loop = !!loop; } else { u_int loop; error = sooptcopyin(sopt, &loop, sizeof loop, sizeof loop); if (error) break; imo->imo_multicast_loop = !!loop; } break; case IP_ADD_MEMBERSHIP: /* * Add a multicast group membership. * Group must be a valid IP multicast address. */ error = sooptcopyin(sopt, &mreq, sizeof mreq, sizeof mreq); if (error) break; if (!IN_MULTICAST(ntohl(mreq.imr_multiaddr.s_addr))) { error = EINVAL; break; } s = splimp(); /* * If no interface address was provided, use the interface of * the route to the given multicast address. */ if (mreq.imr_interface.s_addr == INADDR_ANY) { bzero((caddr_t)&ro, sizeof(ro)); dst = (struct sockaddr_in *)&ro.ro_dst; dst->sin_len = sizeof(*dst); dst->sin_family = AF_INET; dst->sin_addr = mreq.imr_multiaddr; rtalloc(&ro); if (ro.ro_rt == NULL) { error = EADDRNOTAVAIL; splx(s); break; } ifp = ro.ro_rt->rt_ifp; rtfree(ro.ro_rt); } else { INADDR_TO_IFP(mreq.imr_interface, ifp); } /* * See if we found an interface, and confirm that it * supports multicast. */ if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) { error = EADDRNOTAVAIL; splx(s); break; } /* * See if the membership already exists or if all the * membership slots are full. */ for (i = 0; i < imo->imo_num_memberships; ++i) { if (imo->imo_membership[i]->inm_ifp == ifp && imo->imo_membership[i]->inm_addr.s_addr == mreq.imr_multiaddr.s_addr) break; } if (i < imo->imo_num_memberships) { error = EADDRINUSE; splx(s); break; } if (i == IP_MAX_MEMBERSHIPS) { error = ETOOMANYREFS; splx(s); break; } /* * Everything looks good; add a new record to the multicast * address list for the given interface. */ if ((imo->imo_membership[i] = in_addmulti(&mreq.imr_multiaddr, ifp)) == NULL) { error = ENOBUFS; splx(s); break; } ++imo->imo_num_memberships; splx(s); break; case IP_DROP_MEMBERSHIP: /* * Drop a multicast group membership. * Group must be a valid IP multicast address. */ error = sooptcopyin(sopt, &mreq, sizeof mreq, sizeof mreq); if (error) break; if (!IN_MULTICAST(ntohl(mreq.imr_multiaddr.s_addr))) { error = EINVAL; break; } s = splimp(); /* * If an interface address was specified, get a pointer * to its ifnet structure. */ if (mreq.imr_interface.s_addr == INADDR_ANY) ifp = NULL; else { INADDR_TO_IFP(mreq.imr_interface, ifp); if (ifp == NULL) { error = EADDRNOTAVAIL; splx(s); break; } } /* * Find the membership in the membership array. */ for (i = 0; i < imo->imo_num_memberships; ++i) { if ((ifp == NULL || imo->imo_membership[i]->inm_ifp == ifp) && imo->imo_membership[i]->inm_addr.s_addr == mreq.imr_multiaddr.s_addr) break; } if (i == imo->imo_num_memberships) { error = EADDRNOTAVAIL; splx(s); break; } /* * Give up the multicast address record to which the * membership points. */ in_delmulti(imo->imo_membership[i]); /* * Remove the gap in the membership array. */ for (++i; i < imo->imo_num_memberships; ++i) imo->imo_membership[i-1] = imo->imo_membership[i]; --imo->imo_num_memberships; splx(s); break; default: error = EOPNOTSUPP; break; } /* * If all options have default values, no need to keep the mbuf. */ if (imo->imo_multicast_ifp == NULL && imo->imo_multicast_vif == -1 && imo->imo_multicast_ttl == IP_DEFAULT_MULTICAST_TTL && imo->imo_multicast_loop == IP_DEFAULT_MULTICAST_LOOP && imo->imo_num_memberships == 0) { free(*imop, M_IPMOPTS); *imop = NULL; } return (error); } /* * Return the IP multicast options in response to user getsockopt(). */ static int ip_getmoptions(sopt, imo) struct sockopt *sopt; register struct ip_moptions *imo; { struct in_addr addr; struct in_ifaddr *ia; int error, optval; u_char coptval; error = 0; switch (sopt->sopt_name) { case IP_MULTICAST_VIF: if (imo != NULL) optval = imo->imo_multicast_vif; else optval = -1; error = sooptcopyout(sopt, &optval, sizeof optval); break; case IP_MULTICAST_IF: if (imo == NULL || imo->imo_multicast_ifp == NULL) addr.s_addr = INADDR_ANY; else { IFP_TO_IA(imo->imo_multicast_ifp, ia); addr.s_addr = (ia == NULL) ? INADDR_ANY : IA_SIN(ia)->sin_addr.s_addr; } error = sooptcopyout(sopt, &addr, sizeof addr); break; case IP_MULTICAST_TTL: if (imo == 0) optval = coptval = IP_DEFAULT_MULTICAST_TTL; else optval = coptval = imo->imo_multicast_ttl; if (sopt->sopt_valsize == 1) error = sooptcopyout(sopt, &coptval, 1); else error = sooptcopyout(sopt, &optval, sizeof optval); break; case IP_MULTICAST_LOOP: if (imo == 0) optval = coptval = IP_DEFAULT_MULTICAST_LOOP; else optval = coptval = imo->imo_multicast_loop; if (sopt->sopt_valsize == 1) error = sooptcopyout(sopt, &coptval, 1); else error = sooptcopyout(sopt, &optval, sizeof optval); break; default: error = ENOPROTOOPT; break; } return (error); } /* * Discard the IP multicast options. */ void ip_freemoptions(imo) register struct ip_moptions *imo; { register int i; if (imo != NULL) { for (i = 0; i < imo->imo_num_memberships; ++i) in_delmulti(imo->imo_membership[i]); free(imo, M_IPMOPTS); } } /* * Routine called from ip_output() to loop back a copy of an IP multicast * packet to the input queue of a specified interface. Note that this * calls the output routine of the loopback "driver", but with an interface * pointer that might NOT be a loopback interface -- evil, but easier than * replicating that code here. */ static void ip_mloopback(ifp, m, dst, hlen) struct ifnet *ifp; register struct mbuf *m; register struct sockaddr_in *dst; int hlen; { register struct ip *ip; struct mbuf *copym; copym = m_copy(m, 0, M_COPYALL); if (copym != NULL && (copym->m_flags & M_EXT || copym->m_len < hlen)) copym = m_pullup(copym, hlen); if (copym != NULL) { /* * We don't bother to fragment if the IP length is greater * than the interface's MTU. Can this possibly matter? */ ip = mtod(copym, struct ip *); ip->ip_len = htons((u_short)ip->ip_len); ip->ip_off = htons((u_short)ip->ip_off); ip->ip_sum = 0; if (ip->ip_vhl == IP_VHL_BORING) { ip->ip_sum = in_cksum_hdr(ip); } else { ip->ip_sum = in_cksum(copym, hlen); } /* * NB: * It's not clear whether there are any lingering * reentrancy problems in other areas which might * be exposed by using ip_input directly (in * particular, everything which modifies the packet * in-place). Yet another option is using the * protosw directly to deliver the looped back * packet. For the moment, we'll err on the side * of safety by using if_simloop(). */ #if 1 /* XXX */ if (dst->sin_family != AF_INET) { printf("ip_mloopback: bad address family %d\n", dst->sin_family); dst->sin_family = AF_INET; } #endif #ifdef notdef copym->m_pkthdr.rcvif = ifp; ip_input(copym); #else /* if the checksum hasn't been computed, mark it as valid */ if (copym->m_pkthdr.csum_flags & CSUM_DELAY_DATA) { copym->m_pkthdr.csum_flags |= CSUM_DATA_VALID | CSUM_PSEUDO_HDR; copym->m_pkthdr.csum_data = 0xffff; } if_simloop(ifp, copym, dst->sin_family, 0); #endif } } Index: head/sys/netinet/ip_var.h =================================================================== --- head/sys/netinet/ip_var.h (revision 62586) +++ head/sys/netinet/ip_var.h (revision 62587) @@ -1,196 +1,193 @@ /* * Copyright (c) 1982, 1986, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)ip_var.h 8.2 (Berkeley) 1/9/95 * $FreeBSD$ */ #ifndef _NETINET_IP_VAR_H_ #define _NETINET_IP_VAR_H_ /* * Overlay for ip header used by other protocols (tcp, udp). */ struct ipovly { u_char ih_x1[9]; /* (unused) */ u_char ih_pr; /* protocol */ u_short ih_len; /* protocol length */ struct in_addr ih_src; /* source internet address */ struct in_addr ih_dst; /* destination internet address */ }; /* * Ip reassembly queue structure. Each fragment * being reassembled is attached to one of these structures. * They are timed out after ipq_ttl drops to 0, and may also * be reclaimed if memory becomes tight. */ struct ipq { struct ipq *next,*prev; /* to other reass headers */ u_char ipq_ttl; /* time for reass q to live */ u_char ipq_p; /* protocol of this fragment */ u_short ipq_id; /* sequence id for reassembly */ struct mbuf *ipq_frags; /* to ip headers of fragments */ struct in_addr ipq_src,ipq_dst; #ifdef IPDIVERT u_int32_t ipq_div_info; /* ipfw divert port & flags */ u_int16_t ipq_div_cookie; /* ipfw divert cookie */ #endif }; /* * Structure stored in mbuf in inpcb.ip_options * and passed to ip_output when ip options are in use. * The actual length of the options (including ipopt_dst) * is in m_len. */ #define MAX_IPOPTLEN 40 struct ipoption { struct in_addr ipopt_dst; /* first-hop dst if source routed */ char ipopt_list[MAX_IPOPTLEN]; /* options proper */ }; /* * Structure attached to inpcb.ip_moptions and * passed to ip_output when IP multicast options are in use. */ struct ip_moptions { struct ifnet *imo_multicast_ifp; /* ifp for outgoing multicasts */ u_char imo_multicast_ttl; /* TTL for outgoing multicasts */ u_char imo_multicast_loop; /* 1 => hear sends if a member */ u_short imo_num_memberships; /* no. memberships this socket */ struct in_multi *imo_membership[IP_MAX_MEMBERSHIPS]; u_long imo_multicast_vif; /* vif num outgoing multicasts */ }; struct ipstat { u_long ips_total; /* total packets received */ u_long ips_badsum; /* checksum bad */ u_long ips_tooshort; /* packet too short */ u_long ips_toosmall; /* not enough data */ u_long ips_badhlen; /* ip header length < data size */ u_long ips_badlen; /* ip length < ip header length */ u_long ips_fragments; /* fragments received */ u_long ips_fragdropped; /* frags dropped (dups, out of space) */ u_long ips_fragtimeout; /* fragments timed out */ u_long ips_forward; /* packets forwarded */ u_long ips_fastforward; /* packets fast forwarded */ u_long ips_cantforward; /* packets rcvd for unreachable dest */ u_long ips_redirectsent; /* packets forwarded on same net */ u_long ips_noproto; /* unknown or unsupported protocol */ u_long ips_delivered; /* datagrams delivered to upper level*/ u_long ips_localout; /* total ip packets generated here */ u_long ips_odropped; /* lost packets due to nobufs, etc. */ u_long ips_reassembled; /* total packets reassembled ok */ u_long ips_fragmented; /* datagrams successfully fragmented */ u_long ips_ofragments; /* output fragments created */ u_long ips_cantfrag; /* don't fragment flag was set, etc. */ u_long ips_badoptions; /* error in option processing */ u_long ips_noroute; /* packets discarded due to no route */ u_long ips_badvers; /* ip version != 4 */ u_long ips_rawout; /* total raw ip packets generated */ u_long ips_toolong; /* ip length > max ip packet size */ u_long ips_notmember; /* multicasts for unregistered grps */ u_long ips_nogif; /* no match gif found */ }; #ifdef _KERNEL /* flags passed to ip_output as last parameter */ #define IP_FORWARDING 0x1 /* most of ip header exists */ #define IP_RAWOUTPUT 0x2 /* raw ip header exists */ #define IP_ROUTETOIF SO_DONTROUTE /* bypass routing tables */ #define IP_ALLOWBROADCAST SO_BROADCAST /* can send broadcast packets */ -#define IP_SOCKINMRCVIF 0x100 /* IPSEC hack; - * socket pointer in sending - * packet's m_pkthdr.rcvif */ struct ip; struct inpcb; struct route; struct sockopt; extern struct ipstat ipstat; extern u_short ip_id; /* ip packet ctr, for ids */ extern int ip_defttl; /* default IP ttl */ extern int ipforwarding; /* ip forwarding */ extern u_char ip_protox[]; extern struct socket *ip_rsvpd; /* reservation protocol daemon */ extern struct socket *ip_mrouter; /* multicast routing daemon */ extern int (*legal_vif_num) __P((int)); extern u_long (*ip_mcast_src) __P((int)); extern int rsvp_on; extern struct pr_usrreqs rip_usrreqs; int ip_ctloutput __P((struct socket *, struct sockopt *sopt)); void ip_drain __P((void)); void ip_freemoptions __P((struct ip_moptions *)); void ip_init __P((void)); extern int (*ip_mforward) __P((struct ip *, struct ifnet *, struct mbuf *, struct ip_moptions *)); int ip_output __P((struct mbuf *, struct mbuf *, struct route *, int, struct ip_moptions *)); void ip_savecontrol __P((struct inpcb *, struct mbuf **, struct ip *, struct mbuf *)); void ip_slowtimo __P((void)); struct mbuf * ip_srcroute __P((void)); void ip_stripoptions __P((struct mbuf *, struct mbuf *)); int rip_ctloutput __P((struct socket *, struct sockopt *)); void rip_ctlinput __P((int, struct sockaddr *, void *)); void rip_init __P((void)); void rip_input __P((struct mbuf *, int, int)); int rip_output __P((struct mbuf *, struct socket *, u_long)); void ipip_input __P((struct mbuf *, int, int)); void rsvp_input __P((struct mbuf *, int, int)); int ip_rsvp_init __P((struct socket *)); int ip_rsvp_done __P((void)); int ip_rsvp_vif_init __P((struct socket *, struct sockopt *)); int ip_rsvp_vif_done __P((struct socket *, struct sockopt *)); void ip_rsvp_force_done __P((struct socket *)); #ifdef IPDIVERT void div_init __P((void)); void div_input __P((struct mbuf *, int, int)); void divert_packet __P((struct mbuf *, int, int)); extern struct pr_usrreqs div_usrreqs; extern u_int16_t ip_divert_cookie; #endif extern struct sockaddr_in *ip_fw_fwd_addr; void in_delayed_cksum(struct mbuf *m); #endif /* _KERNEL */ #endif /* !_NETINET_IP_VAR_H_ */ Index: head/sys/netinet/raw_ip.c =================================================================== --- head/sys/netinet/raw_ip.c (revision 62586) +++ head/sys/netinet/raw_ip.c (revision 62587) @@ -1,662 +1,661 @@ /* * Copyright (c) 1982, 1986, 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)raw_ip.c 8.7 (Berkeley) 5/15/95 * $FreeBSD$ */ #include "opt_inet6.h" #include "opt_ipsec.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #define _IP_VHL #include #include #include #include #include #include #include #include #ifdef IPSEC #include #endif /*IPSEC*/ #include "opt_ipdn.h" #ifdef DUMMYNET #include #endif struct inpcbhead ripcb; struct inpcbinfo ripcbinfo; /* * Nominal space allocated to a raw ip socket. */ #define RIPSNDQ 8192 #define RIPRCVQ 8192 /* * Raw interface to IP protocol. */ /* * Initialize raw connection block q. */ void rip_init() { LIST_INIT(&ripcb); ripcbinfo.listhead = &ripcb; /* * XXX We don't use the hash list for raw IP, but it's easier * to allocate a one entry hash list than it is to check all * over the place for hashbase == NULL. */ ripcbinfo.hashbase = hashinit(1, M_PCB, &ripcbinfo.hashmask); ripcbinfo.porthashbase = hashinit(1, M_PCB, &ripcbinfo.porthashmask); ripcbinfo.ipi_zone = zinit("ripcb", sizeof(struct inpcb), maxsockets, ZONE_INTERRUPT, 0); } static struct sockaddr_in ripsrc = { sizeof(ripsrc), AF_INET }; /* * Setup generic address and protocol structures * for raw_input routine, then pass them along with * mbuf chain. */ void rip_input(m, off, proto) struct mbuf *m; int off, proto; { register struct ip *ip = mtod(m, struct ip *); register struct inpcb *inp; struct inpcb *last = 0; struct mbuf *opts = 0; ripsrc.sin_addr = ip->ip_src; LIST_FOREACH(inp, &ripcb, inp_list) { #ifdef INET6 if ((inp->inp_vflag & INP_IPV4) == 0) continue; #endif if (inp->inp_ip_p && inp->inp_ip_p != proto) continue; if (inp->inp_laddr.s_addr && inp->inp_laddr.s_addr != ip->ip_dst.s_addr) continue; if (inp->inp_faddr.s_addr && inp->inp_faddr.s_addr != ip->ip_src.s_addr) continue; if (last) { struct mbuf *n = m_copy(m, 0, (int)M_COPYALL); if (n) { if (last->inp_flags & INP_CONTROLOPTS || last->inp_socket->so_options & SO_TIMESTAMP) ip_savecontrol(last, &opts, ip, n); if (sbappendaddr(&last->inp_socket->so_rcv, (struct sockaddr *)&ripsrc, n, opts) == 0) { /* should notify about lost packet */ m_freem(n); if (opts) m_freem(opts); } else sorwakeup(last->inp_socket); opts = 0; } } last = inp; } if (last) { if (last->inp_flags & INP_CONTROLOPTS || last->inp_socket->so_options & SO_TIMESTAMP) ip_savecontrol(last, &opts, ip, m); if (sbappendaddr(&last->inp_socket->so_rcv, (struct sockaddr *)&ripsrc, m, opts) == 0) { m_freem(m); if (opts) m_freem(opts); } else sorwakeup(last->inp_socket); } else { m_freem(m); ipstat.ips_noproto++; ipstat.ips_delivered--; } } /* * Generate IP header and pass packet to ip_output. * Tack on options user may have setup with control call. */ int rip_output(m, so, dst) struct mbuf *m; struct socket *so; u_long dst; { register struct ip *ip; register struct inpcb *inp = sotoinpcb(so); int flags = (so->so_options & SO_DONTROUTE) | IP_ALLOWBROADCAST; /* * If the user handed us a complete IP packet, use it. * Otherwise, allocate an mbuf for a header and fill it in. */ if ((inp->inp_flags & INP_HDRINCL) == 0) { if (m->m_pkthdr.len + sizeof(struct ip) > IP_MAXPACKET) { m_freem(m); return(EMSGSIZE); } M_PREPEND(m, sizeof(struct ip), M_WAIT); ip = mtod(m, struct ip *); ip->ip_tos = 0; ip->ip_off = 0; ip->ip_p = inp->inp_ip_p; ip->ip_len = m->m_pkthdr.len; ip->ip_src = inp->inp_laddr; ip->ip_dst.s_addr = dst; ip->ip_ttl = MAXTTL; } else { if (m->m_pkthdr.len > IP_MAXPACKET) { m_freem(m); return(EMSGSIZE); } ip = mtod(m, struct ip *); /* don't allow both user specified and setsockopt options, and don't allow packet length sizes that will crash */ if (((IP_VHL_HL(ip->ip_vhl) != (sizeof (*ip) >> 2)) && inp->inp_options) || (ip->ip_len > m->m_pkthdr.len) || (ip->ip_len < (IP_VHL_HL(ip->ip_vhl) << 2))) { m_freem(m); return EINVAL; } if (ip->ip_id == 0) ip->ip_id = htons(ip_id++); /* XXX prevent ip_output from overwriting header fields */ flags |= IP_RAWOUTPUT; ipstat.ips_rawout++; } #ifdef IPSEC - m->m_pkthdr.rcvif = (struct ifnet *)so; /*XXX*/ + ipsec_setsocket(m, so); #endif /*IPSEC*/ - return (ip_output(m, inp->inp_options, &inp->inp_route, - flags | IP_SOCKINMRCVIF, + return (ip_output(m, inp->inp_options, &inp->inp_route, flags, inp->inp_moptions)); } /* * Raw IP socket option processing. */ int rip_ctloutput(so, sopt) struct socket *so; struct sockopt *sopt; { struct inpcb *inp = sotoinpcb(so); int error, optval; if (sopt->sopt_level != IPPROTO_IP) return (EINVAL); error = 0; switch (sopt->sopt_dir) { case SOPT_GET: switch (sopt->sopt_name) { case IP_HDRINCL: optval = inp->inp_flags & INP_HDRINCL; error = sooptcopyout(sopt, &optval, sizeof optval); break; case IP_FW_GET: if (ip_fw_ctl_ptr == 0) error = ENOPROTOOPT; else error = ip_fw_ctl_ptr(sopt); break; #ifdef DUMMYNET case IP_DUMMYNET_GET: if (ip_dn_ctl_ptr == NULL) error = ENOPROTOOPT ; else error = ip_dn_ctl_ptr(sopt); break ; #endif /* DUMMYNET */ case MRT_INIT: case MRT_DONE: case MRT_ADD_VIF: case MRT_DEL_VIF: case MRT_ADD_MFC: case MRT_DEL_MFC: case MRT_VERSION: case MRT_ASSERT: error = ip_mrouter_get(so, sopt); break; default: error = ip_ctloutput(so, sopt); break; } break; case SOPT_SET: switch (sopt->sopt_name) { case IP_HDRINCL: error = sooptcopyin(sopt, &optval, sizeof optval, sizeof optval); if (error) break; if (optval) inp->inp_flags |= INP_HDRINCL; else inp->inp_flags &= ~INP_HDRINCL; break; case IP_FW_ADD: case IP_FW_DEL: case IP_FW_FLUSH: case IP_FW_ZERO: case IP_FW_RESETLOG: if (ip_fw_ctl_ptr == 0) error = ENOPROTOOPT; else error = ip_fw_ctl_ptr(sopt); break; #ifdef DUMMYNET case IP_DUMMYNET_CONFIGURE: case IP_DUMMYNET_DEL: case IP_DUMMYNET_FLUSH: if (ip_dn_ctl_ptr == NULL) error = ENOPROTOOPT ; else error = ip_dn_ctl_ptr(sopt); break ; #endif case IP_RSVP_ON: error = ip_rsvp_init(so); break; case IP_RSVP_OFF: error = ip_rsvp_done(); break; /* XXX - should be combined */ case IP_RSVP_VIF_ON: error = ip_rsvp_vif_init(so, sopt); break; case IP_RSVP_VIF_OFF: error = ip_rsvp_vif_done(so, sopt); break; case MRT_INIT: case MRT_DONE: case MRT_ADD_VIF: case MRT_DEL_VIF: case MRT_ADD_MFC: case MRT_DEL_MFC: case MRT_VERSION: case MRT_ASSERT: error = ip_mrouter_set(so, sopt); break; default: error = ip_ctloutput(so, sopt); break; } break; } return (error); } /* * This function exists solely to receive the PRC_IFDOWN messages which * are sent by if_down(). It looks for an ifaddr whose ifa_addr is sa, * and calls in_ifadown() to remove all routes corresponding to that address. * It also receives the PRC_IFUP messages from if_up() and reinstalls the * interface routes. */ void rip_ctlinput(cmd, sa, vip) int cmd; struct sockaddr *sa; void *vip; { struct in_ifaddr *ia; struct ifnet *ifp; int err; int flags; switch (cmd) { case PRC_IFDOWN: for (ia = in_ifaddrhead.tqh_first; ia; ia = ia->ia_link.tqe_next) { if (ia->ia_ifa.ifa_addr == sa && (ia->ia_flags & IFA_ROUTE)) { /* * in_ifscrub kills the interface route. */ in_ifscrub(ia->ia_ifp, ia); /* * in_ifadown gets rid of all the rest of * the routes. This is not quite the right * thing to do, but at least if we are running * a routing process they will come back. */ in_ifadown(&ia->ia_ifa); break; } } break; case PRC_IFUP: for (ia = in_ifaddrhead.tqh_first; ia; ia = ia->ia_link.tqe_next) { if (ia->ia_ifa.ifa_addr == sa) break; } if (ia == 0 || (ia->ia_flags & IFA_ROUTE)) return; flags = RTF_UP; ifp = ia->ia_ifa.ifa_ifp; if ((ifp->if_flags & IFF_LOOPBACK) || (ifp->if_flags & IFF_POINTOPOINT)) flags |= RTF_HOST; err = rtinit(&ia->ia_ifa, RTM_ADD, flags); if (err == 0) ia->ia_flags |= IFA_ROUTE; break; } } u_long rip_sendspace = RIPSNDQ; u_long rip_recvspace = RIPRCVQ; SYSCTL_INT(_net_inet_raw, OID_AUTO, maxdgram, CTLFLAG_RW, &rip_sendspace, 0, "Maximum outgoing raw IP datagram size"); SYSCTL_INT(_net_inet_raw, OID_AUTO, recvspace, CTLFLAG_RW, &rip_recvspace, 0, "Maximum incoming raw IP datagram size"); static int rip_attach(struct socket *so, int proto, struct proc *p) { struct inpcb *inp; int error, s; inp = sotoinpcb(so); if (inp) panic("rip_attach"); if (p && (error = suser(p)) != 0) return error; error = soreserve(so, rip_sendspace, rip_recvspace); if (error) return error; s = splnet(); error = in_pcballoc(so, &ripcbinfo, p); splx(s); if (error) return error; inp = (struct inpcb *)so->so_pcb; inp->inp_vflag |= INP_IPV4; inp->inp_ip_p = proto; #ifdef IPSEC error = ipsec_init_policy(so, &inp->inp_sp); if (error != 0) { in_pcbdetach(inp); return error; } #endif /*IPSEC*/ return 0; } static int rip_detach(struct socket *so) { struct inpcb *inp; inp = sotoinpcb(so); if (inp == 0) panic("rip_detach"); if (so == ip_mrouter) ip_mrouter_done(); ip_rsvp_force_done(so); if (so == ip_rsvpd) ip_rsvp_done(); in_pcbdetach(inp); return 0; } static int rip_abort(struct socket *so) { soisdisconnected(so); return rip_detach(so); } static int rip_disconnect(struct socket *so) { if ((so->so_state & SS_ISCONNECTED) == 0) return ENOTCONN; return rip_abort(so); } static int rip_bind(struct socket *so, struct sockaddr *nam, struct proc *p) { struct inpcb *inp = sotoinpcb(so); struct sockaddr_in *addr = (struct sockaddr_in *)nam; if (nam->sa_len != sizeof(*addr)) return EINVAL; if (TAILQ_EMPTY(&ifnet) || ((addr->sin_family != AF_INET) && (addr->sin_family != AF_IMPLINK)) || (addr->sin_addr.s_addr && ifa_ifwithaddr((struct sockaddr *)addr) == 0)) return EADDRNOTAVAIL; inp->inp_laddr = addr->sin_addr; return 0; } static int rip_connect(struct socket *so, struct sockaddr *nam, struct proc *p) { struct inpcb *inp = sotoinpcb(so); struct sockaddr_in *addr = (struct sockaddr_in *)nam; if (nam->sa_len != sizeof(*addr)) return EINVAL; if (TAILQ_EMPTY(&ifnet)) return EADDRNOTAVAIL; if ((addr->sin_family != AF_INET) && (addr->sin_family != AF_IMPLINK)) return EAFNOSUPPORT; inp->inp_faddr = addr->sin_addr; soisconnected(so); return 0; } static int rip_shutdown(struct socket *so) { socantsendmore(so); return 0; } static int rip_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam, struct mbuf *control, struct proc *p) { struct inpcb *inp = sotoinpcb(so); register u_long dst; if (so->so_state & SS_ISCONNECTED) { if (nam) { m_freem(m); return EISCONN; } dst = inp->inp_faddr.s_addr; } else { if (nam == NULL) { m_freem(m); return ENOTCONN; } dst = ((struct sockaddr_in *)nam)->sin_addr.s_addr; } return rip_output(m, so, dst); } static int rip_pcblist(SYSCTL_HANDLER_ARGS) { int error, i, n, s; struct inpcb *inp, **inp_list; inp_gen_t gencnt; struct xinpgen xig; /* * The process of preparing the TCB list is too time-consuming and * resource-intensive to repeat twice on every request. */ if (req->oldptr == 0) { n = ripcbinfo.ipi_count; req->oldidx = 2 * (sizeof xig) + (n + n/8) * sizeof(struct xinpcb); return 0; } if (req->newptr != 0) return EPERM; /* * OK, now we're committed to doing something. */ s = splnet(); gencnt = ripcbinfo.ipi_gencnt; n = ripcbinfo.ipi_count; splx(s); xig.xig_len = sizeof xig; xig.xig_count = n; xig.xig_gen = gencnt; xig.xig_sogen = so_gencnt; error = SYSCTL_OUT(req, &xig, sizeof xig); if (error) return error; inp_list = malloc(n * sizeof *inp_list, M_TEMP, M_WAITOK); if (inp_list == 0) return ENOMEM; s = splnet(); for (inp = ripcbinfo.listhead->lh_first, i = 0; inp && i < n; inp = inp->inp_list.le_next) { if (inp->inp_gencnt <= gencnt) inp_list[i++] = inp; } splx(s); n = i; error = 0; for (i = 0; i < n; i++) { inp = inp_list[i]; if (inp->inp_gencnt <= gencnt) { struct xinpcb xi; xi.xi_len = sizeof xi; /* XXX should avoid extra copy */ bcopy(inp, &xi.xi_inp, sizeof *inp); if (inp->inp_socket) sotoxsocket(inp->inp_socket, &xi.xi_socket); error = SYSCTL_OUT(req, &xi, sizeof xi); } } if (!error) { /* * Give the user an updated idea of our state. * If the generation differs from what we told * her before, she knows that something happened * while we were processing this request, and it * might be necessary to retry. */ s = splnet(); xig.xig_gen = ripcbinfo.ipi_gencnt; xig.xig_sogen = so_gencnt; xig.xig_count = ripcbinfo.ipi_count; splx(s); error = SYSCTL_OUT(req, &xig, sizeof xig); } free(inp_list, M_TEMP); return error; } SYSCTL_PROC(_net_inet_raw, OID_AUTO/*XXX*/, pcblist, CTLFLAG_RD, 0, 0, rip_pcblist, "S,xinpcb", "List of active raw IP sockets"); struct pr_usrreqs rip_usrreqs = { rip_abort, pru_accept_notsupp, rip_attach, rip_bind, rip_connect, pru_connect2_notsupp, in_control, rip_detach, rip_disconnect, pru_listen_notsupp, in_setpeeraddr, pru_rcvd_notsupp, pru_rcvoob_notsupp, rip_send, pru_sense_null, rip_shutdown, in_setsockaddr, sosend, soreceive, sopoll }; Index: head/sys/netinet/tcp_debug.c =================================================================== --- head/sys/netinet/tcp_debug.c (revision 62586) +++ head/sys/netinet/tcp_debug.c (revision 62587) @@ -1,231 +1,231 @@ /* * Copyright (c) 1982, 1986, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)tcp_debug.c 8.1 (Berkeley) 6/10/93 * $FreeBSD$ */ #include "opt_inet.h" #include "opt_inet6.h" #include "opt_tcpdebug.h" #ifndef INET #error The option TCPDEBUG requires option INET. #endif #ifdef TCPDEBUG /* load symbolic names */ #define PRUREQUESTS #define TCPSTATES #define TCPTIMERS #define TANAMES #endif #include #include #include #include #include #include #include #ifdef INET6 -#include +#include #endif #include #include #include #include #include #include #include #ifdef TCPDEBUG static int tcpconsdebug = 0; #endif static struct tcp_debug tcp_debug[TCP_NDEBUG]; static int tcp_debx; /* * Tcp debug routines */ void tcp_trace(act, ostate, tp, ipgen, th, req) short act, ostate; struct tcpcb *tp; void *ipgen; struct tcphdr *th; int req; { #ifdef INET6 int isipv6; #endif /* INET6 */ tcp_seq seq, ack; int len, flags; struct tcp_debug *td = &tcp_debug[tcp_debx++]; #ifdef INET6 isipv6 = (ipgen != NULL && ((struct ip *)ipgen)->ip_v == 6) ? 1 : 0; #endif /* INET6 */ td->td_family = #ifdef INET6 (isipv6 != 0) ? AF_INET6 : #endif AF_INET; if (tcp_debx == TCP_NDEBUG) tcp_debx = 0; td->td_time = iptime(); td->td_act = act; td->td_ostate = ostate; td->td_tcb = (caddr_t)tp; if (tp) td->td_cb = *tp; else bzero((caddr_t)&td->td_cb, sizeof (*tp)); if (ipgen) { switch (td->td_family) { case AF_INET: bcopy((caddr_t)ipgen, (caddr_t)&td->td_ti.ti_i, sizeof(td->td_ti.ti_i)); bzero((caddr_t)td->td_ip6buf, sizeof(td->td_ip6buf)); break; #ifdef INET6 case AF_INET6: bcopy((caddr_t)ipgen, (caddr_t)td->td_ip6buf, sizeof(td->td_ip6buf)); bzero((caddr_t)&td->td_ti.ti_i, sizeof(td->td_ti.ti_i)); break; #endif default: bzero((caddr_t)td->td_ip6buf, sizeof(td->td_ip6buf)); bzero((caddr_t)&td->td_ti.ti_i, sizeof(td->td_ti.ti_i)); break; } } else { bzero((caddr_t)&td->td_ti.ti_i, sizeof(td->td_ti.ti_i)); bzero((caddr_t)td->td_ip6buf, sizeof(td->td_ip6buf)); } if (th) { switch (td->td_family) { case AF_INET: td->td_ti.ti_t = *th; bzero((caddr_t)&td->td_ti6.th, sizeof(td->td_ti6.th)); break; #ifdef INET6 case AF_INET6: td->td_ti6.th = *th; bzero((caddr_t)&td->td_ti.ti_t, sizeof(td->td_ti.ti_t)); break; #endif default: bzero((caddr_t)&td->td_ti.ti_t, sizeof(td->td_ti.ti_t)); bzero((caddr_t)&td->td_ti6.th, sizeof(td->td_ti6.th)); break; } } else { bzero((caddr_t)&td->td_ti.ti_t, sizeof(td->td_ti.ti_t)); bzero((caddr_t)&td->td_ti6.th, sizeof(td->td_ti6.th)); } td->td_req = req; #ifdef TCPDEBUG if (tcpconsdebug == 0) return; if (tp) printf("%p %s:", tp, tcpstates[ostate]); else printf("???????? "); printf("%s ", tanames[act]); switch (act) { case TA_INPUT: case TA_OUTPUT: case TA_DROP: if (ipgen == NULL || th == NULL) break; seq = th->th_seq; ack = th->th_ack; len = #ifdef INET6 isipv6 ? ((struct ip6_hdr *)ipgen)->ip6_plen : #endif ((struct ip *)ipgen)->ip_len; if (act == TA_OUTPUT) { seq = ntohl(seq); ack = ntohl(ack); len = ntohs((u_short)len); } if (act == TA_OUTPUT) len -= sizeof (struct tcphdr); if (len) printf("[%x..%x)", seq, seq+len); else printf("%x", seq); printf("@%x, urp=%x", ack, th->th_urp); flags = th->th_flags; if (flags) { char *cp = "<"; #define pf(f) { \ if (th->th_flags & TH_##f) { \ printf("%s%s", cp, #f); \ cp = ","; \ } \ } pf(SYN); pf(ACK); pf(FIN); pf(RST); pf(PUSH); pf(URG); printf(">"); } break; case TA_USER: printf("%s", prurequests[req&0xff]); if ((req & 0xff) == PRU_SLOWTIMO) printf("<%s>", tcptimers[req>>8]); break; } if (tp) printf(" -> %s", tcpstates[tp->t_state]); /* print out internal state of tp !?! */ printf("\n"); if (tp == 0) return; printf( "\trcv_(nxt,wnd,up) (%lx,%lx,%lx) snd_(una,nxt,max) (%lx,%lx,%lx)\n", (u_long)tp->rcv_nxt, tp->rcv_wnd, (u_long)tp->rcv_up, (u_long)tp->snd_una, (u_long)tp->snd_nxt, (u_long)tp->snd_max); printf("\tsnd_(wl1,wl2,wnd) (%lx,%lx,%lx)\n", (u_long)tp->snd_wl1, (u_long)tp->snd_wl2, tp->snd_wnd); #endif /* TCPDEBUG */ } Index: head/sys/netinet/tcp_input.c =================================================================== --- head/sys/netinet/tcp_input.c (revision 62586) +++ head/sys/netinet/tcp_input.c (revision 62587) @@ -1,2859 +1,2855 @@ /* * Copyright (c) 1982, 1986, 1988, 1990, 1993, 1994, 1995 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)tcp_input.c 8.12 (Berkeley) 5/24/95 * $FreeBSD$ */ #include "opt_ipfw.h" /* for ipfw_fwd */ #include "opt_inet6.h" #include "opt_ipsec.h" #include "opt_tcpdebug.h" #include "opt_tcp_input.h" #include #include #include #include #include #include #include /* for proc0 declaration */ #include #include #include #include #include /* before tcp_seq.h, for tcp_random18() */ #include #include #include #include #include -#include -#ifdef INET6 -#include +#include /* for ICMP_BANDLIM */ #include -#include -#include -#endif +#include /* for ICMP_BANDLIM */ #include -#ifdef INET6 -#include -#endif #include #ifdef INET6 +#include +#include +#include #include +#include #endif -#include #include #include #include #include #include #ifdef INET6 #include #endif #include #ifdef TCPDEBUG #include u_char tcp_saveipgen[40]; /* the size must be of max ip header, now IPv6 */ struct tcphdr tcp_savetcp; #endif /* TCPDEBUG */ #ifdef IPSEC #include #ifdef INET6 #include #endif #include #endif /*IPSEC*/ #include MALLOC_DEFINE(M_TSEGQ, "tseg_qent", "TCP segment queue entry"); static int tcprexmtthresh = 3; tcp_seq tcp_iss; tcp_cc tcp_ccgen; struct tcpstat tcpstat; SYSCTL_STRUCT(_net_inet_tcp, TCPCTL_STATS, stats, CTLFLAG_RD, &tcpstat , tcpstat, "TCP statistics (struct tcpstat, netinet/tcp_var.h)"); static int log_in_vain = 0; SYSCTL_INT(_net_inet_tcp, OID_AUTO, log_in_vain, CTLFLAG_RW, &log_in_vain, 0, "Log all incoming TCP connections"); static int blackhole = 0; SYSCTL_INT(_net_inet_tcp, OID_AUTO, blackhole, CTLFLAG_RW, &blackhole, 0, "Do not send RST when dropping refused connections"); int tcp_delack_enabled = 1; SYSCTL_INT(_net_inet_tcp, OID_AUTO, delayed_ack, CTLFLAG_RW, &tcp_delack_enabled, 0, "Delay ACK to try and piggyback it onto a data packet"); #ifdef TCP_DROP_SYNFIN static int drop_synfin = 0; SYSCTL_INT(_net_inet_tcp, OID_AUTO, drop_synfin, CTLFLAG_RW, &drop_synfin, 0, "Drop TCP packets with SYN+FIN set"); #endif #ifdef TCP_RESTRICT_RST static int restrict_rst = 0; SYSCTL_INT(_net_inet_tcp, OID_AUTO, restrict_rst, CTLFLAG_RW, &restrict_rst, 0, "Restrict RST emission"); #endif struct inpcbhead tcb; #define tcb6 tcb /* for KAME src sync over BSD*'s */ struct inpcbinfo tcbinfo; static void tcp_dooptions __P((struct tcpcb *, u_char *, int, struct tcphdr *, struct tcpopt *)); static void tcp_pulloutofband __P((struct socket *, struct tcphdr *, struct mbuf *, int)); static int tcp_reass __P((struct tcpcb *, struct tcphdr *, int *, struct mbuf *)); static void tcp_xmit_timer __P((struct tcpcb *, int)); static int tcp_newreno __P((struct tcpcb *, struct tcphdr *)); /* Neighbor Discovery, Neighbor Unreachability Detection Upper layer hint. */ #ifdef INET6 #define ND6_HINT(tp) \ do { \ if ((tp) && (tp)->t_inpcb && \ ((tp)->t_inpcb->inp_vflag & INP_IPV6) != 0 && \ (tp)->t_inpcb->in6p_route.ro_rt) \ - nd6_nud_hint((tp)->t_inpcb->in6p_route.ro_rt, NULL); \ + nd6_nud_hint((tp)->t_inpcb->in6p_route.ro_rt, NULL, 0); \ } while (0) #else #define ND6_HINT(tp) #endif /* * Insert segment which inludes th into reassembly queue of tcp with * control block tp. Return TH_FIN if reassembly now includes * a segment with FIN. The macro form does the common case inline * (segment is the next to be received on an established connection, * and the queue is empty), avoiding linkage into and removal * from the queue and repetition of various conversions. * Set DELACK for segments received in order, but ack immediately * when segments are out of order (so fast retransmit can work). */ #define TCP_REASS(tp, th, tlenp, m, so, flags) { \ if ((th)->th_seq == (tp)->rcv_nxt && \ LIST_EMPTY(&(tp)->t_segq) && \ (tp)->t_state == TCPS_ESTABLISHED) { \ if (tcp_delack_enabled) \ callout_reset(tp->tt_delack, tcp_delacktime, \ tcp_timer_delack, tp); \ else \ tp->t_flags |= TF_ACKNOW; \ (tp)->rcv_nxt += *(tlenp); \ flags = (th)->th_flags & TH_FIN; \ tcpstat.tcps_rcvpack++;\ tcpstat.tcps_rcvbyte += *(tlenp);\ ND6_HINT(tp); \ sbappend(&(so)->so_rcv, (m)); \ sorwakeup(so); \ } else { \ (flags) = tcp_reass((tp), (th), (tlenp), (m)); \ tp->t_flags |= TF_ACKNOW; \ } \ } static int tcp_reass(tp, th, tlenp, m) register struct tcpcb *tp; register struct tcphdr *th; int *tlenp; struct mbuf *m; { struct tseg_qent *q; struct tseg_qent *p = NULL; struct tseg_qent *nq; struct tseg_qent *te; struct socket *so = tp->t_inpcb->inp_socket; int flags; /* * Call with th==0 after become established to * force pre-ESTABLISHED data up to user socket. */ if (th == 0) goto present; /* Allocate a new queue entry. If we can't, just drop the pkt. XXX */ MALLOC(te, struct tseg_qent *, sizeof (struct tseg_qent), M_TSEGQ, M_NOWAIT); if (te == NULL) { tcpstat.tcps_rcvmemdrop++; m_freem(m); return (0); } /* * Find a segment which begins after this one does. */ LIST_FOREACH(q, &tp->t_segq, tqe_q) { if (SEQ_GT(q->tqe_th->th_seq, th->th_seq)) break; p = q; } /* * If there is a preceding segment, it may provide some of * our data already. If so, drop the data from the incoming * segment. If it provides all of our data, drop us. */ if (p != NULL) { register int i; /* conversion to int (in i) handles seq wraparound */ i = p->tqe_th->th_seq + p->tqe_len - th->th_seq; if (i > 0) { if (i >= *tlenp) { tcpstat.tcps_rcvduppack++; tcpstat.tcps_rcvdupbyte += *tlenp; m_freem(m); FREE(te, M_TSEGQ); /* * Try to present any queued data * at the left window edge to the user. * This is needed after the 3-WHS * completes. */ goto present; /* ??? */ } m_adj(m, i); *tlenp -= i; th->th_seq += i; } } tcpstat.tcps_rcvoopack++; tcpstat.tcps_rcvoobyte += *tlenp; /* * While we overlap succeeding segments trim them or, * if they are completely covered, dequeue them. */ while (q) { register int i = (th->th_seq + *tlenp) - q->tqe_th->th_seq; if (i <= 0) break; if (i < q->tqe_len) { q->tqe_th->th_seq += i; q->tqe_len -= i; m_adj(q->tqe_m, i); break; } nq = LIST_NEXT(q, tqe_q); LIST_REMOVE(q, tqe_q); m_freem(q->tqe_m); FREE(q, M_TSEGQ); q = nq; } /* Insert the new segment queue entry into place. */ te->tqe_m = m; te->tqe_th = th; te->tqe_len = *tlenp; if (p == NULL) { LIST_INSERT_HEAD(&tp->t_segq, te, tqe_q); } else { LIST_INSERT_AFTER(p, te, tqe_q); } present: /* * Present data to user, advancing rcv_nxt through * completed sequence space. */ if (!TCPS_HAVEESTABLISHED(tp->t_state)) return (0); q = LIST_FIRST(&tp->t_segq); if (!q || q->tqe_th->th_seq != tp->rcv_nxt) return (0); do { tp->rcv_nxt += q->tqe_len; flags = q->tqe_th->th_flags & TH_FIN; nq = LIST_NEXT(q, tqe_q); LIST_REMOVE(q, tqe_q); if (so->so_state & SS_CANTRCVMORE) m_freem(q->tqe_m); else sbappend(&so->so_rcv, q->tqe_m); FREE(q, M_TSEGQ); q = nq; } while (q && q->tqe_th->th_seq == tp->rcv_nxt); ND6_HINT(tp); sorwakeup(so); return (flags); } /* * TCP input routine, follows pages 65-76 of the * protocol specification dated September, 1981 very closely. */ #ifdef INET6 int tcp6_input(mp, offp, proto) struct mbuf **mp; int *offp, proto; { register struct mbuf *m = *mp; IP6_EXTHDR_CHECK(m, *offp, sizeof(struct tcphdr), IPPROTO_DONE); /* * draft-itojun-ipv6-tcp-to-anycast * better place to put this in? */ if (m->m_flags & M_ANYCAST6) { struct ip6_hdr *ip6; ip6 = mtod(m, struct ip6_hdr *); icmp6_error(m, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADDR, (caddr_t)&ip6->ip6_dst - (caddr_t)ip6); return IPPROTO_DONE; } tcp_input(m, *offp, proto); return IPPROTO_DONE; } #endif void tcp_input(m, off0, proto) register struct mbuf *m; int off0, proto; { register struct tcphdr *th; register struct ip *ip = NULL; register struct ipovly *ipov; register struct inpcb *inp; u_char *optp = NULL; int optlen = 0; int len, tlen, off; int drop_hdrlen; register struct tcpcb *tp = 0; register int thflags; struct socket *so = 0; int todrop, acked, ourfinisacked, needoutput = 0; struct in_addr laddr; #ifdef INET6 struct in6_addr laddr6; #endif int dropsocket = 0; int iss = 0; u_long tiwin; struct tcpopt to; /* options in this segment */ struct rmxp_tao *taop; /* pointer to our TAO cache entry */ struct rmxp_tao tao_noncached; /* in case there's no cached entry */ #ifdef TCPDEBUG short ostate = 0; #endif #ifdef INET6 struct ip6_hdr *ip6 = NULL; int isipv6; #endif /* INET6 */ #ifdef INET6 isipv6 = (mtod(m, struct ip *)->ip_v == 6) ? 1 : 0; #endif bzero((char *)&to, sizeof(to)); tcpstat.tcps_rcvtotal++; #ifdef INET6 if (isipv6) { /* IP6_EXTHDR_CHECK() is already done at tcp6_input() */ ip6 = mtod(m, struct ip6_hdr *); tlen = sizeof(*ip6) + ntohs(ip6->ip6_plen) - off0; if (in6_cksum(m, IPPROTO_TCP, off0, tlen)) { tcpstat.tcps_rcvbadsum++; goto drop; } th = (struct tcphdr *)((caddr_t)ip6 + off0); } else #endif /* INET6 */ { /* * Get IP and TCP header together in first mbuf. * Note: IP leaves IP header in first mbuf. */ if (off0 > sizeof (struct ip)) { ip_stripoptions(m, (struct mbuf *)0); off0 = sizeof(struct ip); } if (m->m_len < sizeof (struct tcpiphdr)) { if ((m = m_pullup(m, sizeof (struct tcpiphdr))) == 0) { tcpstat.tcps_rcvshort++; return; } } ip = mtod(m, struct ip *); ipov = (struct ipovly *)ip; th = (struct tcphdr *)((caddr_t)ip + off0); tlen = ip->ip_len; if (m->m_pkthdr.csum_flags & CSUM_DATA_VALID) { if (m->m_pkthdr.csum_flags & CSUM_PSEUDO_HDR) th->th_sum = m->m_pkthdr.csum_data; else th->th_sum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr, htonl(m->m_pkthdr.csum_data + ip->ip_len + IPPROTO_TCP)); th->th_sum ^= 0xffff; } else { /* * Checksum extended TCP header and data. */ len = sizeof (struct ip) + tlen; bzero(ipov->ih_x1, sizeof(ipov->ih_x1)); ipov->ih_len = (u_short)tlen; HTONS(ipov->ih_len); th->th_sum = in_cksum(m, len); } if (th->th_sum) { tcpstat.tcps_rcvbadsum++; goto drop; } #ifdef INET6 /* Re-initialization for later version check */ ip->ip_v = IPVERSION; #endif } /* * Check that TCP offset makes sense, * pull out TCP options and adjust length. XXX */ off = th->th_off << 2; if (off < sizeof (struct tcphdr) || off > tlen) { tcpstat.tcps_rcvbadoff++; goto drop; } tlen -= off; /* tlen is used instead of ti->ti_len */ if (off > sizeof (struct tcphdr)) { #ifdef INET6 if (isipv6) { IP6_EXTHDR_CHECK(m, off0, off, ); ip6 = mtod(m, struct ip6_hdr *); th = (struct tcphdr *)((caddr_t)ip6 + off0); } else #endif /* INET6 */ { if (m->m_len < sizeof(struct ip) + off) { if ((m = m_pullup(m, sizeof (struct ip) + off)) == 0) { tcpstat.tcps_rcvshort++; return; } ip = mtod(m, struct ip *); ipov = (struct ipovly *)ip; th = (struct tcphdr *)((caddr_t)ip + off0); } } optlen = off - sizeof (struct tcphdr); optp = (u_char *)(th + 1); } thflags = th->th_flags; #ifdef TCP_DROP_SYNFIN /* * If the drop_synfin option is enabled, drop all packets with * both the SYN and FIN bits set. This prevents e.g. nmap from * identifying the TCP/IP stack. * * This is incompatible with RFC1644 extensions (T/TCP). */ if (drop_synfin && (thflags & (TH_SYN|TH_FIN)) == (TH_SYN|TH_FIN)) goto drop; #endif /* * Convert TCP protocol specific fields to host format. */ NTOHL(th->th_seq); NTOHL(th->th_ack); NTOHS(th->th_win); NTOHS(th->th_urp); /* * Delay droping TCP, IP headers, IPv6 ext headers, and TCP options, * until after ip6_savecontrol() is called and before other functions * which don't want those proto headers. * Because ip6_savecontrol() is going to parse the mbuf to * search for data to be passed up to user-land, it wants mbuf * parameters to be unchanged. */ drop_hdrlen = off0 + off; /* * Locate pcb for segment. */ findpcb: #ifdef IPFIREWALL_FORWARD if (ip_fw_fwd_addr != NULL #ifdef INET6 && isipv6 == NULL /* IPv6 support is not yet */ #endif /* INET6 */ ) { /* * Diverted. Pretend to be the destination. * already got one like this? */ inp = in_pcblookup_hash(&tcbinfo, ip->ip_src, th->th_sport, ip->ip_dst, th->th_dport, 0, m->m_pkthdr.rcvif); if (!inp) { /* * No, then it's new. Try find the ambushing socket */ if (!ip_fw_fwd_addr->sin_port) { inp = in_pcblookup_hash(&tcbinfo, ip->ip_src, th->th_sport, ip_fw_fwd_addr->sin_addr, th->th_dport, 1, m->m_pkthdr.rcvif); } else { inp = in_pcblookup_hash(&tcbinfo, ip->ip_src, th->th_sport, ip_fw_fwd_addr->sin_addr, ntohs(ip_fw_fwd_addr->sin_port), 1, m->m_pkthdr.rcvif); } } ip_fw_fwd_addr = NULL; } else #endif /* IPFIREWALL_FORWARD */ { #ifdef INET6 if (isipv6) inp = in6_pcblookup_hash(&tcbinfo, &ip6->ip6_src, th->th_sport, &ip6->ip6_dst, th->th_dport, 1, m->m_pkthdr.rcvif); else #endif /* INET6 */ inp = in_pcblookup_hash(&tcbinfo, ip->ip_src, th->th_sport, ip->ip_dst, th->th_dport, 1, m->m_pkthdr.rcvif); } #ifdef IPSEC #ifdef INET6 if (isipv6) { if (inp != NULL && ipsec6_in_reject_so(m, inp->inp_socket)) { ipsec6stat.in_polvio++; goto drop; } } else #endif /* INET6 */ if (inp != NULL && ipsec4_in_reject_so(m, inp->inp_socket)) { ipsecstat.in_polvio++; goto drop; } #endif /*IPSEC*/ /* * If the state is CLOSED (i.e., TCB does not exist) then * all data in the incoming segment is discarded. * If the TCB exists but is in CLOSED state, it is embryonic, * but should either do a listen or a connect soon. */ if (inp == NULL) { if (log_in_vain) { #ifdef INET6 char dbuf[INET6_ADDRSTRLEN], sbuf[INET6_ADDRSTRLEN]; #else /* INET6 */ char dbuf[4*sizeof "123"], sbuf[4*sizeof "123"]; #endif /* INET6 */ #ifdef INET6 if (isipv6) { strcpy(dbuf, ip6_sprintf(&ip6->ip6_dst)); strcpy(sbuf, ip6_sprintf(&ip6->ip6_src)); } else #endif { strcpy(dbuf, inet_ntoa(ip->ip_dst)); strcpy(sbuf, inet_ntoa(ip->ip_src)); } switch (log_in_vain) { case 1: if(thflags & TH_SYN) log(LOG_INFO, "Connection attempt to TCP %s:%d from %s:%d\n", dbuf, ntohs(th->th_dport), sbuf, ntohs(th->th_sport)); break; case 2: log(LOG_INFO, "Connection attempt to TCP %s:%d from %s:%d flags:0x%x\n", dbuf, ntohs(th->th_dport), sbuf, ntohs(th->th_sport), thflags); break; default: break; } } if (blackhole) { switch (blackhole) { case 1: if (thflags & TH_SYN) goto drop; break; case 2: goto drop; default: goto drop; } } goto maybedropwithreset; } tp = intotcpcb(inp); if (tp == 0) goto maybedropwithreset; if (tp->t_state == TCPS_CLOSED) goto drop; /* Unscale the window into a 32-bit value. */ if ((thflags & TH_SYN) == 0) tiwin = th->th_win << tp->snd_scale; else tiwin = th->th_win; #ifdef INET6 /* save packet options if user wanted */ if (isipv6 && inp->in6p_flags & INP_CONTROLOPTS) { if (inp->in6p_options) { m_freem(inp->in6p_options); inp->in6p_options = 0; } ip6_savecontrol(inp, &inp->in6p_options, ip6, m); } /* else, should also do ip_srcroute() here? */ #endif /* INET6 */ so = inp->inp_socket; if (so->so_options & (SO_DEBUG|SO_ACCEPTCONN)) { #ifdef TCPDEBUG if (so->so_options & SO_DEBUG) { ostate = tp->t_state; #ifdef INET6 if (isipv6) bcopy((char *)ip6, (char *)tcp_saveipgen, sizeof(*ip6)); else #endif /* INET6 */ bcopy((char *)ip, (char *)tcp_saveipgen, sizeof(*ip)); tcp_savetcp = *th; } #endif if (so->so_options & SO_ACCEPTCONN) { register struct tcpcb *tp0 = tp; struct socket *so2; #ifdef IPSEC struct socket *oso; #endif #ifdef INET6 struct inpcb *oinp = sotoinpcb(so); #endif /* INET6 */ #ifndef IPSEC /* * Current IPsec implementation makes incorrect IPsec * cache if this check is done here. * So delay this until duplicated socket is created. */ if ((thflags & (TH_RST|TH_ACK|TH_SYN)) != TH_SYN) { /* * Note: dropwithreset makes sure we don't * send a RST in response to a RST. */ if (thflags & TH_ACK) { tcpstat.tcps_badsyn++; goto maybedropwithreset; } goto drop; } #endif so2 = sonewconn(so, 0); if (so2 == 0) { tcpstat.tcps_listendrop++; so2 = sodropablereq(so); if (so2) { tcp_drop(sototcpcb(so2), ETIMEDOUT); so2 = sonewconn(so, 0); } if (!so2) goto drop; } #ifdef IPSEC oso = so; #endif so = so2; /* * This is ugly, but .... * * Mark socket as temporary until we're * committed to keeping it. The code at * ``drop'' and ``dropwithreset'' check the * flag dropsocket to see if the temporary * socket created here should be discarded. * We mark the socket as discardable until * we're committed to it below in TCPS_LISTEN. */ dropsocket++; inp = (struct inpcb *)so->so_pcb; #ifdef INET6 if (isipv6) inp->in6p_laddr = ip6->ip6_dst; else { if ((inp->inp_flags & IN6P_BINDV6ONLY) == 0) { inp->inp_vflag &= ~INP_IPV6; inp->inp_vflag |= INP_IPV4; } #endif /* INET6 */ inp->inp_laddr = ip->ip_dst; #ifdef INET6 } #endif /* INET6 */ inp->inp_lport = th->th_dport; if (in_pcbinshash(inp) != 0) { /* * Undo the assignments above if we failed to * put the PCB on the hash lists. */ #ifdef INET6 if (isipv6) inp->in6p_laddr = in6addr_any; else #endif /* INET6 */ inp->inp_laddr.s_addr = INADDR_ANY; inp->inp_lport = 0; goto drop; } #ifdef IPSEC /* * To avoid creating incorrectly cached IPsec * association, this is need to be done here. * * Subject: (KAME-snap 748) * From: Wayne Knowles * ftp://ftp.kame.net/pub/mail-list/snap-users/748 */ if ((thflags & (TH_RST|TH_ACK|TH_SYN)) != TH_SYN) { /* * Note: dropwithreset makes sure we don't * send a RST in response to a RST. */ if (thflags & TH_ACK) { tcpstat.tcps_badsyn++; goto maybedropwithreset; } goto drop; } #endif #ifdef INET6 if (isipv6) { /* * inherit socket options from the listening * socket. */ inp->inp_flags |= oinp->inp_flags & INP_CONTROLOPTS; if (inp->inp_flags & INP_CONTROLOPTS) { if (inp->in6p_options) { m_freem(inp->in6p_options); inp->in6p_options = 0; } ip6_savecontrol(inp, &inp->in6p_options, ip6, m); } } else #endif /* INET6 */ inp->inp_options = ip_srcroute(); #ifdef IPSEC /* copy old policy into new socket's */ if (ipsec_copy_policy(sotoinpcb(oso)->inp_sp, inp->inp_sp)) printf("tcp_input: could not copy policy\n"); #endif tp = intotcpcb(inp); tp->t_state = TCPS_LISTEN; tp->t_flags |= tp0->t_flags & (TF_NOPUSH|TF_NOOPT); /* Compute proper scaling value from buffer space */ while (tp->request_r_scale < TCP_MAX_WINSHIFT && TCP_MAXWIN << tp->request_r_scale < so->so_rcv.sb_hiwat) tp->request_r_scale++; } } /* * Segment received on connection. * Reset idle time and keep-alive timer. */ tp->t_rcvtime = ticks; if (TCPS_HAVEESTABLISHED(tp->t_state)) callout_reset(tp->tt_keep, tcp_keepidle, tcp_timer_keep, tp); /* * Process options if not in LISTEN state, * else do it below (after getting remote address). */ if (tp->t_state != TCPS_LISTEN) tcp_dooptions(tp, optp, optlen, th, &to); /* * Header prediction: check for the two common cases * of a uni-directional data xfer. If the packet has * no control flags, is in-sequence, the window didn't * change and we're not retransmitting, it's a * candidate. If the length is zero and the ack moved * forward, we're the sender side of the xfer. Just * free the data acked & wake any higher level process * that was blocked waiting for space. If the length * is non-zero and the ack didn't move, we're the * receiver side. If we're getting packets in-order * (the reassembly queue is empty), add the data to * the socket buffer and note that we need a delayed ack. * Make sure that the hidden state-flags are also off. * Since we check for TCPS_ESTABLISHED above, it can only * be TH_NEEDSYN. */ if (tp->t_state == TCPS_ESTABLISHED && (thflags & (TH_SYN|TH_FIN|TH_RST|TH_URG|TH_ACK)) == TH_ACK && ((tp->t_flags & (TF_NEEDSYN|TF_NEEDFIN)) == 0) && ((to.to_flag & TOF_TS) == 0 || TSTMP_GEQ(to.to_tsval, tp->ts_recent)) && /* * Using the CC option is compulsory if once started: * the segment is OK if no T/TCP was negotiated or * if the segment has a CC option equal to CCrecv */ ((tp->t_flags & (TF_REQ_CC|TF_RCVD_CC)) != (TF_REQ_CC|TF_RCVD_CC) || ((to.to_flag & TOF_CC) != 0 && to.to_cc == tp->cc_recv)) && th->th_seq == tp->rcv_nxt && tiwin && tiwin == tp->snd_wnd && tp->snd_nxt == tp->snd_max) { /* * If last ACK falls within this segment's sequence numbers, * record the timestamp. * NOTE that the test is modified according to the latest * proposal of the tcplw@cray.com list (Braden 1993/04/26). */ if ((to.to_flag & TOF_TS) != 0 && SEQ_LEQ(th->th_seq, tp->last_ack_sent)) { tp->ts_recent_age = ticks; tp->ts_recent = to.to_tsval; } if (tlen == 0) { if (SEQ_GT(th->th_ack, tp->snd_una) && SEQ_LEQ(th->th_ack, tp->snd_max) && tp->snd_cwnd >= tp->snd_wnd && tp->t_dupacks < tcprexmtthresh) { /* * this is a pure ack for outstanding data. */ ++tcpstat.tcps_predack; /* * "bad retransmit" recovery */ if (tp->t_rxtshift == 1 && ticks < tp->t_badrxtwin) { tp->snd_cwnd = tp->snd_cwnd_prev; tp->snd_ssthresh = tp->snd_ssthresh_prev; tp->snd_nxt = tp->snd_max; tp->t_badrxtwin = 0; } if ((to.to_flag & TOF_TS) != 0) tcp_xmit_timer(tp, ticks - to.to_tsecr + 1); else if (tp->t_rtttime && SEQ_GT(th->th_ack, tp->t_rtseq)) tcp_xmit_timer(tp, ticks - tp->t_rtttime); acked = th->th_ack - tp->snd_una; tcpstat.tcps_rcvackpack++; tcpstat.tcps_rcvackbyte += acked; sbdrop(&so->so_snd, acked); tp->snd_una = th->th_ack; m_freem(m); ND6_HINT(tp); /* some progress has been done */ /* * If all outstanding data are acked, stop * retransmit timer, otherwise restart timer * using current (possibly backed-off) value. * If process is waiting for space, * wakeup/selwakeup/signal. If data * are ready to send, let tcp_output * decide between more output or persist. */ if (tp->snd_una == tp->snd_max) callout_stop(tp->tt_rexmt); else if (!callout_active(tp->tt_persist)) callout_reset(tp->tt_rexmt, tp->t_rxtcur, tcp_timer_rexmt, tp); sowwakeup(so); if (so->so_snd.sb_cc) (void) tcp_output(tp); return; } } else if (th->th_ack == tp->snd_una && LIST_EMPTY(&tp->t_segq) && tlen <= sbspace(&so->so_rcv)) { /* * this is a pure, in-sequence data packet * with nothing on the reassembly queue and * we have enough buffer space to take it. */ ++tcpstat.tcps_preddat; tp->rcv_nxt += tlen; tcpstat.tcps_rcvpack++; tcpstat.tcps_rcvbyte += tlen; ND6_HINT(tp); /* some progress has been done */ /* * Add data to socket buffer. */ m_adj(m, drop_hdrlen); /* delayed header drop */ sbappend(&so->so_rcv, m); sorwakeup(so); if (tcp_delack_enabled) { callout_reset(tp->tt_delack, tcp_delacktime, tcp_timer_delack, tp); } else { tp->t_flags |= TF_ACKNOW; tcp_output(tp); } return; } } /* * Calculate amount of space in receive window, * and then do TCP input processing. * Receive window is amount of space in rcv queue, * but not less than advertised window. */ { int win; win = sbspace(&so->so_rcv); if (win < 0) win = 0; tp->rcv_wnd = imax(win, (int)(tp->rcv_adv - tp->rcv_nxt)); } switch (tp->t_state) { /* * If the state is LISTEN then ignore segment if it contains an RST. * If the segment contains an ACK then it is bad and send a RST. * If it does not contain a SYN then it is not interesting; drop it. * If it is from this socket, drop it, it must be forged. * Don't bother responding if the destination was a broadcast. * Otherwise initialize tp->rcv_nxt, and tp->irs, select an initial * tp->iss, and send a segment: * * Also initialize tp->snd_nxt to tp->iss+1 and tp->snd_una to tp->iss. * Fill in remote peer address fields if not previously specified. * Enter SYN_RECEIVED state, and process any other fields of this * segment in this state. */ case TCPS_LISTEN: { register struct sockaddr_in *sin; #ifdef INET6 register struct sockaddr_in6 *sin6; #endif if (thflags & TH_RST) goto drop; if (thflags & TH_ACK) goto maybedropwithreset; if ((thflags & TH_SYN) == 0) goto drop; if (th->th_dport == th->th_sport) { #ifdef INET6 if (isipv6) { if (IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &ip6->ip6_src)) goto drop; } else #endif /* INET6 */ if (ip->ip_dst.s_addr == ip->ip_src.s_addr) goto drop; } /* * RFC1122 4.2.3.10, p. 104: discard bcast/mcast SYN * in_broadcast() should never return true on a received * packet with M_BCAST not set. * * Packets with a multicast source address should also * be discarded. */ if (m->m_flags & (M_BCAST|M_MCAST)) goto drop; #ifdef INET6 if (isipv6) { if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) || IN6_IS_ADDR_MULTICAST(&ip6->ip6_src)) goto drop; } else #endif if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr)) || IN_MULTICAST(ntohl(ip->ip_src.s_addr)) || ip->ip_src.s_addr == htonl(INADDR_BROADCAST)) goto drop; #ifdef INET6 if (isipv6) { MALLOC(sin6, struct sockaddr_in6 *, sizeof *sin6, M_SONAME, M_NOWAIT); if (sin6 == NULL) goto drop; bzero(sin6, sizeof(*sin6)); sin6->sin6_family = AF_INET6; sin6->sin6_len = sizeof(*sin6); sin6->sin6_addr = ip6->ip6_src; sin6->sin6_port = th->th_sport; laddr6 = inp->in6p_laddr; if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr)) inp->in6p_laddr = ip6->ip6_dst; if (in6_pcbconnect(inp, (struct sockaddr *)sin6, &proc0)) { inp->in6p_laddr = laddr6; FREE(sin6, M_SONAME); goto drop; } FREE(sin6, M_SONAME); } else #endif { MALLOC(sin, struct sockaddr_in *, sizeof *sin, M_SONAME, M_NOWAIT); if (sin == NULL) goto drop; sin->sin_family = AF_INET; sin->sin_len = sizeof(*sin); sin->sin_addr = ip->ip_src; sin->sin_port = th->th_sport; bzero((caddr_t)sin->sin_zero, sizeof(sin->sin_zero)); laddr = inp->inp_laddr; if (inp->inp_laddr.s_addr == INADDR_ANY) inp->inp_laddr = ip->ip_dst; if (in_pcbconnect(inp, (struct sockaddr *)sin, &proc0)) { inp->inp_laddr = laddr; FREE(sin, M_SONAME); goto drop; } FREE(sin, M_SONAME); } tp->t_template = tcp_template(tp); if (tp->t_template == 0) { tp = tcp_drop(tp, ENOBUFS); dropsocket = 0; /* socket is already gone */ goto drop; } if ((taop = tcp_gettaocache(inp)) == NULL) { taop = &tao_noncached; bzero(taop, sizeof(*taop)); } tcp_dooptions(tp, optp, optlen, th, &to); if (iss) tp->iss = iss; else tp->iss = tcp_iss; tcp_iss += TCP_ISSINCR/4; tp->irs = th->th_seq; tcp_sendseqinit(tp); tcp_rcvseqinit(tp); tp->snd_recover = tp->snd_una; /* * Initialization of the tcpcb for transaction; * set SND.WND = SEG.WND, * initialize CCsend and CCrecv. */ tp->snd_wnd = tiwin; /* initial send-window */ tp->cc_send = CC_INC(tcp_ccgen); tp->cc_recv = to.to_cc; /* * Perform TAO test on incoming CC (SEG.CC) option, if any. * - compare SEG.CC against cached CC from the same host, * if any. * - if SEG.CC > chached value, SYN must be new and is accepted * immediately: save new CC in the cache, mark the socket * connected, enter ESTABLISHED state, turn on flag to * send a SYN in the next segment. * A virtual advertised window is set in rcv_adv to * initialize SWS prevention. Then enter normal segment * processing: drop SYN, process data and FIN. * - otherwise do a normal 3-way handshake. */ if ((to.to_flag & TOF_CC) != 0) { if (((tp->t_flags & TF_NOPUSH) != 0) && taop->tao_cc != 0 && CC_GT(to.to_cc, taop->tao_cc)) { taop->tao_cc = to.to_cc; tp->t_starttime = ticks; tp->t_state = TCPS_ESTABLISHED; /* * If there is a FIN, or if there is data and the * connection is local, then delay SYN,ACK(SYN) in * the hope of piggy-backing it on a response * segment. Otherwise must send ACK now in case * the other side is slow starting. */ if (tcp_delack_enabled && ((thflags & TH_FIN) || (tlen != 0 && #ifdef INET6 ((isipv6 && in6_localaddr(&inp->in6p_faddr)) || (!isipv6 && #endif in_localaddr(inp->inp_faddr) #ifdef INET6 )) #endif ))) { callout_reset(tp->tt_delack, tcp_delacktime, tcp_timer_delack, tp); tp->t_flags |= TF_NEEDSYN; } else tp->t_flags |= (TF_ACKNOW | TF_NEEDSYN); /* * Limit the `virtual advertised window' to TCP_MAXWIN * here. Even if we requested window scaling, it will * become effective only later when our SYN is acked. */ tp->rcv_adv += min(tp->rcv_wnd, TCP_MAXWIN); tcpstat.tcps_connects++; soisconnected(so); callout_reset(tp->tt_keep, tcp_keepinit, tcp_timer_keep, tp); dropsocket = 0; /* committed to socket */ tcpstat.tcps_accepts++; goto trimthenstep6; } /* else do standard 3-way handshake */ } else { /* * No CC option, but maybe CC.NEW: * invalidate cached value. */ taop->tao_cc = 0; } /* * TAO test failed or there was no CC option, * do a standard 3-way handshake. */ tp->t_flags |= TF_ACKNOW; tp->t_state = TCPS_SYN_RECEIVED; callout_reset(tp->tt_keep, tcp_keepinit, tcp_timer_keep, tp); dropsocket = 0; /* committed to socket */ tcpstat.tcps_accepts++; goto trimthenstep6; } /* * If the state is SYN_RECEIVED: * if seg contains an ACK, but not for our SYN/ACK, send a RST. */ case TCPS_SYN_RECEIVED: if ((thflags & TH_ACK) && (SEQ_LEQ(th->th_ack, tp->snd_una) || SEQ_GT(th->th_ack, tp->snd_max))) goto maybedropwithreset; break; /* * If the state is SYN_SENT: * if seg contains an ACK, but not for our SYN, drop the input. * if seg contains a RST, then drop the connection. * if seg does not contain SYN, then drop it. * Otherwise this is an acceptable SYN segment * initialize tp->rcv_nxt and tp->irs * if seg contains ack then advance tp->snd_una * if SYN has been acked change to ESTABLISHED else SYN_RCVD state * arrange for segment to be acked (eventually) * continue processing rest of data/controls, beginning with URG */ case TCPS_SYN_SENT: if ((taop = tcp_gettaocache(inp)) == NULL) { taop = &tao_noncached; bzero(taop, sizeof(*taop)); } if ((thflags & TH_ACK) && (SEQ_LEQ(th->th_ack, tp->iss) || SEQ_GT(th->th_ack, tp->snd_max))) { /* * If we have a cached CCsent for the remote host, * hence we haven't just crashed and restarted, * do not send a RST. This may be a retransmission * from the other side after our earlier ACK was lost. * Our new SYN, when it arrives, will serve as the * needed ACK. */ if (taop->tao_ccsent != 0) goto drop; else goto dropwithreset; } if (thflags & TH_RST) { if (thflags & TH_ACK) tp = tcp_drop(tp, ECONNREFUSED); goto drop; } if ((thflags & TH_SYN) == 0) goto drop; tp->snd_wnd = th->th_win; /* initial send window */ tp->cc_recv = to.to_cc; /* foreign CC */ tp->irs = th->th_seq; tcp_rcvseqinit(tp); if (thflags & TH_ACK) { /* * Our SYN was acked. If segment contains CC.ECHO * option, check it to make sure this segment really * matches our SYN. If not, just drop it as old * duplicate, but send an RST if we're still playing * by the old rules. If no CC.ECHO option, make sure * we don't get fooled into using T/TCP. */ if (to.to_flag & TOF_CCECHO) { if (tp->cc_send != to.to_ccecho) { if (taop->tao_ccsent != 0) goto drop; else goto dropwithreset; } } else tp->t_flags &= ~TF_RCVD_CC; tcpstat.tcps_connects++; soisconnected(so); /* Do window scaling on this connection? */ if ((tp->t_flags & (TF_RCVD_SCALE|TF_REQ_SCALE)) == (TF_RCVD_SCALE|TF_REQ_SCALE)) { tp->snd_scale = tp->requested_s_scale; tp->rcv_scale = tp->request_r_scale; } /* Segment is acceptable, update cache if undefined. */ if (taop->tao_ccsent == 0) taop->tao_ccsent = to.to_ccecho; tp->rcv_adv += tp->rcv_wnd; tp->snd_una++; /* SYN is acked */ /* * If there's data, delay ACK; if there's also a FIN * ACKNOW will be turned on later. */ if (tcp_delack_enabled && tlen != 0) callout_reset(tp->tt_delack, tcp_delacktime, tcp_timer_delack, tp); else tp->t_flags |= TF_ACKNOW; /* * Received in SYN_SENT[*] state. * Transitions: * SYN_SENT --> ESTABLISHED * SYN_SENT* --> FIN_WAIT_1 */ tp->t_starttime = ticks; if (tp->t_flags & TF_NEEDFIN) { tp->t_state = TCPS_FIN_WAIT_1; tp->t_flags &= ~TF_NEEDFIN; thflags &= ~TH_SYN; } else { tp->t_state = TCPS_ESTABLISHED; callout_reset(tp->tt_keep, tcp_keepidle, tcp_timer_keep, tp); } } else { /* * Received initial SYN in SYN-SENT[*] state => simul- * taneous open. If segment contains CC option and there is * a cached CC, apply TAO test; if it succeeds, connection is * half-synchronized. Otherwise, do 3-way handshake: * SYN-SENT -> SYN-RECEIVED * SYN-SENT* -> SYN-RECEIVED* * If there was no CC option, clear cached CC value. */ tp->t_flags |= TF_ACKNOW; callout_stop(tp->tt_rexmt); if (to.to_flag & TOF_CC) { if (taop->tao_cc != 0 && CC_GT(to.to_cc, taop->tao_cc)) { /* * update cache and make transition: * SYN-SENT -> ESTABLISHED* * SYN-SENT* -> FIN-WAIT-1* */ taop->tao_cc = to.to_cc; tp->t_starttime = ticks; if (tp->t_flags & TF_NEEDFIN) { tp->t_state = TCPS_FIN_WAIT_1; tp->t_flags &= ~TF_NEEDFIN; } else { tp->t_state = TCPS_ESTABLISHED; callout_reset(tp->tt_keep, tcp_keepidle, tcp_timer_keep, tp); } tp->t_flags |= TF_NEEDSYN; } else tp->t_state = TCPS_SYN_RECEIVED; } else { /* CC.NEW or no option => invalidate cache */ taop->tao_cc = 0; tp->t_state = TCPS_SYN_RECEIVED; } } trimthenstep6: /* * Advance th->th_seq to correspond to first data byte. * If data, trim to stay within window, * dropping FIN if necessary. */ th->th_seq++; if (tlen > tp->rcv_wnd) { todrop = tlen - tp->rcv_wnd; m_adj(m, -todrop); tlen = tp->rcv_wnd; thflags &= ~TH_FIN; tcpstat.tcps_rcvpackafterwin++; tcpstat.tcps_rcvbyteafterwin += todrop; } tp->snd_wl1 = th->th_seq - 1; tp->rcv_up = th->th_seq; /* * Client side of transaction: already sent SYN and data. * If the remote host used T/TCP to validate the SYN, * our data will be ACK'd; if so, enter normal data segment * processing in the middle of step 5, ack processing. * Otherwise, goto step 6. */ if (thflags & TH_ACK) goto process_ACK; goto step6; /* * If the state is LAST_ACK or CLOSING or TIME_WAIT: * if segment contains a SYN and CC [not CC.NEW] option: * if state == TIME_WAIT and connection duration > MSL, * drop packet and send RST; * * if SEG.CC > CCrecv then is new SYN, and can implicitly * ack the FIN (and data) in retransmission queue. * Complete close and delete TCPCB. Then reprocess * segment, hoping to find new TCPCB in LISTEN state; * * else must be old SYN; drop it. * else do normal processing. */ case TCPS_LAST_ACK: case TCPS_CLOSING: case TCPS_TIME_WAIT: if ((thflags & TH_SYN) && (to.to_flag & TOF_CC) && tp->cc_recv != 0) { if (tp->t_state == TCPS_TIME_WAIT && (ticks - tp->t_starttime) > tcp_msl) goto dropwithreset; if (CC_GT(to.to_cc, tp->cc_recv)) { tp = tcp_close(tp); goto findpcb; } else goto drop; } break; /* continue normal processing */ } /* * States other than LISTEN or SYN_SENT. * First check the RST flag and sequence number since reset segments * are exempt from the timestamp and connection count tests. This * fixes a bug introduced by the Stevens, vol. 2, p. 960 bugfix * below which allowed reset segments in half the sequence space * to fall though and be processed (which gives forged reset * segments with a random sequence number a 50 percent chance of * killing a connection). * Then check timestamp, if present. * Then check the connection count, if present. * Then check that at least some bytes of segment are within * receive window. If segment begins before rcv_nxt, * drop leading data (and SYN); if nothing left, just ack. * * * If the RST bit is set, check the sequence number to see * if this is a valid reset segment. * RFC 793 page 37: * In all states except SYN-SENT, all reset (RST) segments * are validated by checking their SEQ-fields. A reset is * valid if its sequence number is in the window. * Note: this does not take into account delayed ACKs, so * we should test against last_ack_sent instead of rcv_nxt. * The sequence number in the reset segment is normally an * echo of our outgoing acknowlegement numbers, but some hosts * send a reset with the sequence number at the rightmost edge * of our receive window, and we have to handle this case. * If we have multiple segments in flight, the intial reset * segment sequence numbers will be to the left of last_ack_sent, * but they will eventually catch up. * In any case, it never made sense to trim reset segments to * fit the receive window since RFC 1122 says: * 4.2.2.12 RST Segment: RFC-793 Section 3.4 * * A TCP SHOULD allow a received RST segment to include data. * * DISCUSSION * It has been suggested that a RST segment could contain * ASCII text that encoded and explained the cause of the * RST. No standard has yet been established for such * data. * * If the reset segment passes the sequence number test examine * the state: * SYN_RECEIVED STATE: * If passive open, return to LISTEN state. * If active open, inform user that connection was refused. * ESTABLISHED, FIN_WAIT_1, FIN_WAIT2, CLOSE_WAIT STATES: * Inform user that connection was reset, and close tcb. * CLOSING, LAST_ACK STATES: * Close the tcb. * TIME_WAIT STATE: * Drop the segment - see Stevens, vol. 2, p. 964 and * RFC 1337. */ if (thflags & TH_RST) { if (SEQ_GEQ(th->th_seq, tp->last_ack_sent) && SEQ_LT(th->th_seq, tp->last_ack_sent + tp->rcv_wnd)) { switch (tp->t_state) { case TCPS_SYN_RECEIVED: so->so_error = ECONNREFUSED; goto close; case TCPS_ESTABLISHED: case TCPS_FIN_WAIT_1: case TCPS_FIN_WAIT_2: case TCPS_CLOSE_WAIT: so->so_error = ECONNRESET; close: tp->t_state = TCPS_CLOSED; tcpstat.tcps_drops++; tp = tcp_close(tp); break; case TCPS_CLOSING: case TCPS_LAST_ACK: tp = tcp_close(tp); break; case TCPS_TIME_WAIT: break; } } goto drop; } /* * RFC 1323 PAWS: If we have a timestamp reply on this segment * and it's less than ts_recent, drop it. */ if ((to.to_flag & TOF_TS) != 0 && tp->ts_recent && TSTMP_LT(to.to_tsval, tp->ts_recent)) { /* Check to see if ts_recent is over 24 days old. */ if ((int)(ticks - tp->ts_recent_age) > TCP_PAWS_IDLE) { /* * Invalidate ts_recent. If this segment updates * ts_recent, the age will be reset later and ts_recent * will get a valid value. If it does not, setting * ts_recent to zero will at least satisfy the * requirement that zero be placed in the timestamp * echo reply when ts_recent isn't valid. The * age isn't reset until we get a valid ts_recent * because we don't want out-of-order segments to be * dropped when ts_recent is old. */ tp->ts_recent = 0; } else { tcpstat.tcps_rcvduppack++; tcpstat.tcps_rcvdupbyte += tlen; tcpstat.tcps_pawsdrop++; goto dropafterack; } } /* * T/TCP mechanism * If T/TCP was negotiated and the segment doesn't have CC, * or if its CC is wrong then drop the segment. * RST segments do not have to comply with this. */ if ((tp->t_flags & (TF_REQ_CC|TF_RCVD_CC)) == (TF_REQ_CC|TF_RCVD_CC) && ((to.to_flag & TOF_CC) == 0 || tp->cc_recv != to.to_cc)) goto dropafterack; /* * In the SYN-RECEIVED state, validate that the packet belongs to * this connection before trimming the data to fit the receive * window. Check the sequence number versus IRS since we know * the sequence numbers haven't wrapped. This is a partial fix * for the "LAND" DoS attack. */ if (tp->t_state == TCPS_SYN_RECEIVED && SEQ_LT(th->th_seq, tp->irs)) goto maybedropwithreset; todrop = tp->rcv_nxt - th->th_seq; if (todrop > 0) { if (thflags & TH_SYN) { thflags &= ~TH_SYN; th->th_seq++; if (th->th_urp > 1) th->th_urp--; else thflags &= ~TH_URG; todrop--; } /* * Following if statement from Stevens, vol. 2, p. 960. */ if (todrop > tlen || (todrop == tlen && (thflags & TH_FIN) == 0)) { /* * Any valid FIN must be to the left of the window. * At this point the FIN must be a duplicate or out * of sequence; drop it. */ thflags &= ~TH_FIN; /* * Send an ACK to resynchronize and drop any data. * But keep on processing for RST or ACK. */ tp->t_flags |= TF_ACKNOW; todrop = tlen; tcpstat.tcps_rcvduppack++; tcpstat.tcps_rcvdupbyte += todrop; } else { tcpstat.tcps_rcvpartduppack++; tcpstat.tcps_rcvpartdupbyte += todrop; } drop_hdrlen += todrop; /* drop from the top afterwards */ th->th_seq += todrop; tlen -= todrop; if (th->th_urp > todrop) th->th_urp -= todrop; else { thflags &= ~TH_URG; th->th_urp = 0; } } /* * If new data are received on a connection after the * user processes are gone, then RST the other end. */ if ((so->so_state & SS_NOFDREF) && tp->t_state > TCPS_CLOSE_WAIT && tlen) { tp = tcp_close(tp); tcpstat.tcps_rcvafterclose++; goto dropwithreset; } /* * If segment ends after window, drop trailing data * (and PUSH and FIN); if nothing left, just ACK. */ todrop = (th->th_seq+tlen) - (tp->rcv_nxt+tp->rcv_wnd); if (todrop > 0) { tcpstat.tcps_rcvpackafterwin++; if (todrop >= tlen) { tcpstat.tcps_rcvbyteafterwin += tlen; /* * If a new connection request is received * while in TIME_WAIT, drop the old connection * and start over if the sequence numbers * are above the previous ones. */ if (thflags & TH_SYN && tp->t_state == TCPS_TIME_WAIT && SEQ_GT(th->th_seq, tp->rcv_nxt)) { iss = tp->snd_nxt + TCP_ISSINCR; tp = tcp_close(tp); goto findpcb; } /* * If window is closed can only take segments at * window edge, and have to drop data and PUSH from * incoming segments. Continue processing, but * remember to ack. Otherwise, drop segment * and ack. */ if (tp->rcv_wnd == 0 && th->th_seq == tp->rcv_nxt) { tp->t_flags |= TF_ACKNOW; tcpstat.tcps_rcvwinprobe++; } else goto dropafterack; } else tcpstat.tcps_rcvbyteafterwin += todrop; m_adj(m, -todrop); tlen -= todrop; thflags &= ~(TH_PUSH|TH_FIN); } /* * If last ACK falls within this segment's sequence numbers, * record its timestamp. * NOTE that the test is modified according to the latest * proposal of the tcplw@cray.com list (Braden 1993/04/26). */ if ((to.to_flag & TOF_TS) != 0 && SEQ_LEQ(th->th_seq, tp->last_ack_sent)) { tp->ts_recent_age = ticks; tp->ts_recent = to.to_tsval; } /* * If a SYN is in the window, then this is an * error and we send an RST and drop the connection. */ if (thflags & TH_SYN) { tp = tcp_drop(tp, ECONNRESET); goto dropwithreset; } /* * If the ACK bit is off: if in SYN-RECEIVED state or SENDSYN * flag is on (half-synchronized state), then queue data for * later processing; else drop segment and return. */ if ((thflags & TH_ACK) == 0) { if (tp->t_state == TCPS_SYN_RECEIVED || (tp->t_flags & TF_NEEDSYN)) goto step6; else goto drop; } /* * Ack processing. */ switch (tp->t_state) { /* * In SYN_RECEIVED state, the ack ACKs our SYN, so enter * ESTABLISHED state and continue processing. * The ACK was checked above. */ case TCPS_SYN_RECEIVED: tcpstat.tcps_connects++; soisconnected(so); /* Do window scaling? */ if ((tp->t_flags & (TF_RCVD_SCALE|TF_REQ_SCALE)) == (TF_RCVD_SCALE|TF_REQ_SCALE)) { tp->snd_scale = tp->requested_s_scale; tp->rcv_scale = tp->request_r_scale; } /* * Upon successful completion of 3-way handshake, * update cache.CC if it was undefined, pass any queued * data to the user, and advance state appropriately. */ if ((taop = tcp_gettaocache(inp)) != NULL && taop->tao_cc == 0) taop->tao_cc = tp->cc_recv; /* * Make transitions: * SYN-RECEIVED -> ESTABLISHED * SYN-RECEIVED* -> FIN-WAIT-1 */ tp->t_starttime = ticks; if (tp->t_flags & TF_NEEDFIN) { tp->t_state = TCPS_FIN_WAIT_1; tp->t_flags &= ~TF_NEEDFIN; } else { tp->t_state = TCPS_ESTABLISHED; callout_reset(tp->tt_keep, tcp_keepidle, tcp_timer_keep, tp); } /* * If segment contains data or ACK, will call tcp_reass() * later; if not, do so now to pass queued data to user. */ if (tlen == 0 && (thflags & TH_FIN) == 0) (void) tcp_reass(tp, (struct tcphdr *)0, 0, (struct mbuf *)0); tp->snd_wl1 = th->th_seq - 1; /* fall into ... */ /* * In ESTABLISHED state: drop duplicate ACKs; ACK out of range * ACKs. If the ack is in the range * tp->snd_una < th->th_ack <= tp->snd_max * then advance tp->snd_una to th->th_ack and drop * data from the retransmission queue. If this ACK reflects * more up to date window information we update our window information. */ case TCPS_ESTABLISHED: case TCPS_FIN_WAIT_1: case TCPS_FIN_WAIT_2: case TCPS_CLOSE_WAIT: case TCPS_CLOSING: case TCPS_LAST_ACK: case TCPS_TIME_WAIT: if (SEQ_LEQ(th->th_ack, tp->snd_una)) { if (tlen == 0 && tiwin == tp->snd_wnd) { tcpstat.tcps_rcvdupack++; /* * If we have outstanding data (other than * a window probe), this is a completely * duplicate ack (ie, window info didn't * change), the ack is the biggest we've * seen and we've seen exactly our rexmt * threshhold of them, assume a packet * has been dropped and retransmit it. * Kludge snd_nxt & the congestion * window so we send only this one * packet. * * We know we're losing at the current * window size so do congestion avoidance * (set ssthresh to half the current window * and pull our congestion window back to * the new ssthresh). * * Dup acks mean that packets have left the * network (they're now cached at the receiver) * so bump cwnd by the amount in the receiver * to keep a constant cwnd packets in the * network. */ if (!callout_active(tp->tt_rexmt) || th->th_ack != tp->snd_una) tp->t_dupacks = 0; else if (++tp->t_dupacks == tcprexmtthresh) { tcp_seq onxt = tp->snd_nxt; u_int win = min(tp->snd_wnd, tp->snd_cwnd) / 2 / tp->t_maxseg; if (tcp_do_newreno && SEQ_LT(th->th_ack, tp->snd_recover)) { /* False retransmit, should not * cut window */ tp->snd_cwnd += tp->t_maxseg; tp->t_dupacks = 0; (void) tcp_output(tp); goto drop; } if (win < 2) win = 2; tp->snd_ssthresh = win * tp->t_maxseg; tp->snd_recover = tp->snd_max; callout_stop(tp->tt_rexmt); tp->t_rtttime = 0; tp->snd_nxt = th->th_ack; tp->snd_cwnd = tp->t_maxseg; (void) tcp_output(tp); tp->snd_cwnd = tp->snd_ssthresh + tp->t_maxseg * tp->t_dupacks; if (SEQ_GT(onxt, tp->snd_nxt)) tp->snd_nxt = onxt; goto drop; } else if (tp->t_dupacks > tcprexmtthresh) { tp->snd_cwnd += tp->t_maxseg; (void) tcp_output(tp); goto drop; } } else tp->t_dupacks = 0; break; } /* * If the congestion window was inflated to account * for the other side's cached packets, retract it. */ if (tcp_do_newreno == 0) { if (tp->t_dupacks >= tcprexmtthresh && tp->snd_cwnd > tp->snd_ssthresh) tp->snd_cwnd = tp->snd_ssthresh; tp->t_dupacks = 0; } else if (tp->t_dupacks >= tcprexmtthresh && !tcp_newreno(tp, th)) { /* * Window inflation should have left us with approx. * snd_ssthresh outstanding data. But in case we * would be inclined to send a burst, better to do * it via the slow start mechanism. */ if (SEQ_GT(th->th_ack + tp->snd_ssthresh, tp->snd_max)) tp->snd_cwnd = tp->snd_max - th->th_ack + tp->t_maxseg; else tp->snd_cwnd = tp->snd_ssthresh; tp->t_dupacks = 0; } if (SEQ_GT(th->th_ack, tp->snd_max)) { tcpstat.tcps_rcvacktoomuch++; goto dropafterack; } /* * If we reach this point, ACK is not a duplicate, * i.e., it ACKs something we sent. */ if (tp->t_flags & TF_NEEDSYN) { /* * T/TCP: Connection was half-synchronized, and our * SYN has been ACK'd (so connection is now fully * synchronized). Go to non-starred state, * increment snd_una for ACK of SYN, and check if * we can do window scaling. */ tp->t_flags &= ~TF_NEEDSYN; tp->snd_una++; /* Do window scaling? */ if ((tp->t_flags & (TF_RCVD_SCALE|TF_REQ_SCALE)) == (TF_RCVD_SCALE|TF_REQ_SCALE)) { tp->snd_scale = tp->requested_s_scale; tp->rcv_scale = tp->request_r_scale; } } process_ACK: acked = th->th_ack - tp->snd_una; tcpstat.tcps_rcvackpack++; tcpstat.tcps_rcvackbyte += acked; /* * If we just performed our first retransmit, and the ACK * arrives within our recovery window, then it was a mistake * to do the retransmit in the first place. Recover our * original cwnd and ssthresh, and proceed to transmit where * we left off. */ if (tp->t_rxtshift == 1 && ticks < tp->t_badrxtwin) { tp->snd_cwnd = tp->snd_cwnd_prev; tp->snd_ssthresh = tp->snd_ssthresh_prev; tp->snd_nxt = tp->snd_max; tp->t_badrxtwin = 0; /* XXX probably not required */ } /* * If we have a timestamp reply, update smoothed * round trip time. If no timestamp is present but * transmit timer is running and timed sequence * number was acked, update smoothed round trip time. * Since we now have an rtt measurement, cancel the * timer backoff (cf., Phil Karn's retransmit alg.). * Recompute the initial retransmit timer. */ if (to.to_flag & TOF_TS) tcp_xmit_timer(tp, ticks - to.to_tsecr + 1); else if (tp->t_rtttime && SEQ_GT(th->th_ack, tp->t_rtseq)) tcp_xmit_timer(tp, ticks - tp->t_rtttime); /* * If all outstanding data is acked, stop retransmit * timer and remember to restart (more output or persist). * If there is more data to be acked, restart retransmit * timer, using current (possibly backed-off) value. */ if (th->th_ack == tp->snd_max) { callout_stop(tp->tt_rexmt); needoutput = 1; } else if (!callout_active(tp->tt_persist)) callout_reset(tp->tt_rexmt, tp->t_rxtcur, tcp_timer_rexmt, tp); /* * If no data (only SYN) was ACK'd, * skip rest of ACK processing. */ if (acked == 0) goto step6; /* * When new data is acked, open the congestion window. * If the window gives us less than ssthresh packets * in flight, open exponentially (maxseg per packet). * Otherwise open linearly: maxseg per window * (maxseg^2 / cwnd per packet). */ { register u_int cw = tp->snd_cwnd; register u_int incr = tp->t_maxseg; if (cw > tp->snd_ssthresh) incr = incr * incr / cw; if (tcp_do_newreno == 0 || SEQ_GEQ(th->th_ack, tp->snd_recover)) tp->snd_cwnd = min(cw + incr,TCP_MAXWIN<snd_scale); } if (acked > so->so_snd.sb_cc) { tp->snd_wnd -= so->so_snd.sb_cc; sbdrop(&so->so_snd, (int)so->so_snd.sb_cc); ourfinisacked = 1; } else { sbdrop(&so->so_snd, acked); tp->snd_wnd -= acked; ourfinisacked = 0; } sowwakeup(so); tp->snd_una = th->th_ack; if (SEQ_LT(tp->snd_nxt, tp->snd_una)) tp->snd_nxt = tp->snd_una; switch (tp->t_state) { /* * In FIN_WAIT_1 STATE in addition to the processing * for the ESTABLISHED state if our FIN is now acknowledged * then enter FIN_WAIT_2. */ case TCPS_FIN_WAIT_1: if (ourfinisacked) { /* * If we can't receive any more * data, then closing user can proceed. * Starting the timer is contrary to the * specification, but if we don't get a FIN * we'll hang forever. */ if (so->so_state & SS_CANTRCVMORE) { soisdisconnected(so); callout_reset(tp->tt_2msl, tcp_maxidle, tcp_timer_2msl, tp); } tp->t_state = TCPS_FIN_WAIT_2; } break; /* * In CLOSING STATE in addition to the processing for * the ESTABLISHED state if the ACK acknowledges our FIN * then enter the TIME-WAIT state, otherwise ignore * the segment. */ case TCPS_CLOSING: if (ourfinisacked) { tp->t_state = TCPS_TIME_WAIT; tcp_canceltimers(tp); /* Shorten TIME_WAIT [RFC-1644, p.28] */ if (tp->cc_recv != 0 && (ticks - tp->t_starttime) < tcp_msl) callout_reset(tp->tt_2msl, tp->t_rxtcur * TCPTV_TWTRUNC, tcp_timer_2msl, tp); else callout_reset(tp->tt_2msl, 2 * tcp_msl, tcp_timer_2msl, tp); soisdisconnected(so); } break; /* * In LAST_ACK, we may still be waiting for data to drain * and/or to be acked, as well as for the ack of our FIN. * If our FIN is now acknowledged, delete the TCB, * enter the closed state and return. */ case TCPS_LAST_ACK: if (ourfinisacked) { tp = tcp_close(tp); goto drop; } break; /* * In TIME_WAIT state the only thing that should arrive * is a retransmission of the remote FIN. Acknowledge * it and restart the finack timer. */ case TCPS_TIME_WAIT: callout_reset(tp->tt_2msl, 2 * tcp_msl, tcp_timer_2msl, tp); goto dropafterack; } } step6: /* * Update window information. * Don't look at window if no ACK: TAC's send garbage on first SYN. */ if ((thflags & TH_ACK) && (SEQ_LT(tp->snd_wl1, th->th_seq) || (tp->snd_wl1 == th->th_seq && (SEQ_LT(tp->snd_wl2, th->th_ack) || (tp->snd_wl2 == th->th_ack && tiwin > tp->snd_wnd))))) { /* keep track of pure window updates */ if (tlen == 0 && tp->snd_wl2 == th->th_ack && tiwin > tp->snd_wnd) tcpstat.tcps_rcvwinupd++; tp->snd_wnd = tiwin; tp->snd_wl1 = th->th_seq; tp->snd_wl2 = th->th_ack; if (tp->snd_wnd > tp->max_sndwnd) tp->max_sndwnd = tp->snd_wnd; needoutput = 1; } /* * Process segments with URG. */ if ((thflags & TH_URG) && th->th_urp && TCPS_HAVERCVDFIN(tp->t_state) == 0) { /* * This is a kludge, but if we receive and accept * random urgent pointers, we'll crash in * soreceive. It's hard to imagine someone * actually wanting to send this much urgent data. */ if (th->th_urp + so->so_rcv.sb_cc > sb_max) { th->th_urp = 0; /* XXX */ thflags &= ~TH_URG; /* XXX */ goto dodata; /* XXX */ } /* * If this segment advances the known urgent pointer, * then mark the data stream. This should not happen * in CLOSE_WAIT, CLOSING, LAST_ACK or TIME_WAIT STATES since * a FIN has been received from the remote side. * In these states we ignore the URG. * * According to RFC961 (Assigned Protocols), * the urgent pointer points to the last octet * of urgent data. We continue, however, * to consider it to indicate the first octet * of data past the urgent section as the original * spec states (in one of two places). */ if (SEQ_GT(th->th_seq+th->th_urp, tp->rcv_up)) { tp->rcv_up = th->th_seq + th->th_urp; so->so_oobmark = so->so_rcv.sb_cc + (tp->rcv_up - tp->rcv_nxt) - 1; if (so->so_oobmark == 0) so->so_state |= SS_RCVATMARK; sohasoutofband(so); tp->t_oobflags &= ~(TCPOOB_HAVEDATA | TCPOOB_HADDATA); } /* * Remove out of band data so doesn't get presented to user. * This can happen independent of advancing the URG pointer, * but if two URG's are pending at once, some out-of-band * data may creep in... ick. */ if (th->th_urp <= (u_long)tlen #ifdef SO_OOBINLINE && (so->so_options & SO_OOBINLINE) == 0 #endif ) tcp_pulloutofband(so, th, m, drop_hdrlen); /* hdr drop is delayed */ } else /* * If no out of band data is expected, * pull receive urgent pointer along * with the receive window. */ if (SEQ_GT(tp->rcv_nxt, tp->rcv_up)) tp->rcv_up = tp->rcv_nxt; dodata: /* XXX */ /* * Process the segment text, merging it into the TCP sequencing queue, * and arranging for acknowledgment of receipt if necessary. * This process logically involves adjusting tp->rcv_wnd as data * is presented to the user (this happens in tcp_usrreq.c, * case PRU_RCVD). If a FIN has already been received on this * connection then we just ignore the text. */ if ((tlen || (thflags&TH_FIN)) && TCPS_HAVERCVDFIN(tp->t_state) == 0) { m_adj(m, drop_hdrlen); /* delayed header drop */ TCP_REASS(tp, th, &tlen, m, so, thflags); /* * Note the amount of data that peer has sent into * our window, in order to estimate the sender's * buffer size. */ len = so->so_rcv.sb_hiwat - (tp->rcv_adv - tp->rcv_nxt); } else { m_freem(m); thflags &= ~TH_FIN; } /* * If FIN is received ACK the FIN and let the user know * that the connection is closing. */ if (thflags & TH_FIN) { if (TCPS_HAVERCVDFIN(tp->t_state) == 0) { socantrcvmore(so); /* * If connection is half-synchronized * (ie NEEDSYN flag on) then delay ACK, * so it may be piggybacked when SYN is sent. * Otherwise, since we received a FIN then no * more input can be expected, send ACK now. */ if (tcp_delack_enabled && (tp->t_flags & TF_NEEDSYN)) callout_reset(tp->tt_delack, tcp_delacktime, tcp_timer_delack, tp); else tp->t_flags |= TF_ACKNOW; tp->rcv_nxt++; } switch (tp->t_state) { /* * In SYN_RECEIVED and ESTABLISHED STATES * enter the CLOSE_WAIT state. */ case TCPS_SYN_RECEIVED: tp->t_starttime = ticks; /*FALLTHROUGH*/ case TCPS_ESTABLISHED: tp->t_state = TCPS_CLOSE_WAIT; break; /* * If still in FIN_WAIT_1 STATE FIN has not been acked so * enter the CLOSING state. */ case TCPS_FIN_WAIT_1: tp->t_state = TCPS_CLOSING; break; /* * In FIN_WAIT_2 state enter the TIME_WAIT state, * starting the time-wait timer, turning off the other * standard timers. */ case TCPS_FIN_WAIT_2: tp->t_state = TCPS_TIME_WAIT; tcp_canceltimers(tp); /* Shorten TIME_WAIT [RFC-1644, p.28] */ if (tp->cc_recv != 0 && (ticks - tp->t_starttime) < tcp_msl) { callout_reset(tp->tt_2msl, tp->t_rxtcur * TCPTV_TWTRUNC, tcp_timer_2msl, tp); /* For transaction client, force ACK now. */ tp->t_flags |= TF_ACKNOW; } else callout_reset(tp->tt_2msl, 2 * tcp_msl, tcp_timer_2msl, tp); soisdisconnected(so); break; /* * In TIME_WAIT state restart the 2 MSL time_wait timer. */ case TCPS_TIME_WAIT: callout_reset(tp->tt_2msl, 2 * tcp_msl, tcp_timer_2msl, tp); break; } } #ifdef TCPDEBUG if (so->so_options & SO_DEBUG) tcp_trace(TA_INPUT, ostate, tp, (void *)tcp_saveipgen, &tcp_savetcp, 0); #endif /* * Return any desired output. */ if (needoutput || (tp->t_flags & TF_ACKNOW)) (void) tcp_output(tp); return; dropafterack: /* * Generate an ACK dropping incoming segment if it occupies * sequence space, where the ACK reflects our state. * * We can now skip the test for the RST flag since all * paths to this code happen after packets containing * RST have been dropped. * * In the SYN-RECEIVED state, don't send an ACK unless the * segment we received passes the SYN-RECEIVED ACK test. * If it fails send a RST. This breaks the loop in the * "LAND" DoS attack, and also prevents an ACK storm * between two listening ports that have been sent forged * SYN segments, each with the source address of the other. */ if (tp->t_state == TCPS_SYN_RECEIVED && (thflags & TH_ACK) && (SEQ_GT(tp->snd_una, th->th_ack) || SEQ_GT(th->th_ack, tp->snd_max)) ) goto maybedropwithreset; #ifdef TCPDEBUG if (so->so_options & SO_DEBUG) tcp_trace(TA_DROP, ostate, tp, (void *)tcp_saveipgen, &tcp_savetcp, 0); #endif m_freem(m); tp->t_flags |= TF_ACKNOW; (void) tcp_output(tp); return; /* * Conditionally drop with reset or just drop depending on whether * we think we are under attack or not. */ maybedropwithreset: if (badport_bandlim(1) < 0) goto drop; /* fall through */ dropwithreset: #ifdef TCP_RESTRICT_RST if (restrict_rst) goto drop; #endif /* * Generate a RST, dropping incoming segment. * Make ACK acceptable to originator of segment. * Don't bother to respond if destination was broadcast/multicast. */ if ((thflags & TH_RST) || m->m_flags & (M_BCAST|M_MCAST)) goto drop; #ifdef INET6 if (isipv6) { if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) || IN6_IS_ADDR_MULTICAST(&ip6->ip6_src)) goto drop; } else #endif /* INET6 */ if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr)) || IN_MULTICAST(ntohl(ip->ip_src.s_addr)) || ip->ip_src.s_addr == htonl(INADDR_BROADCAST)) goto drop; /* IPv6 anycast check is done at tcp6_input() */ #ifdef TCPDEBUG if (tp == 0 || (tp->t_inpcb->inp_socket->so_options & SO_DEBUG)) tcp_trace(TA_DROP, ostate, tp, (void *)tcp_saveipgen, &tcp_savetcp, 0); #endif if (thflags & TH_ACK) /* mtod() below is safe as long as hdr dropping is delayed */ tcp_respond(tp, mtod(m, void *), th, m, (tcp_seq)0, th->th_ack, TH_RST); else { if (thflags & TH_SYN) tlen++; /* mtod() below is safe as long as hdr dropping is delayed */ tcp_respond(tp, mtod(m, void *), th, m, th->th_seq+tlen, (tcp_seq)0, TH_RST|TH_ACK); } /* destroy temporarily created socket */ if (dropsocket) (void) soabort(so); return; drop: /* * Drop space held by incoming segment and return. */ #ifdef TCPDEBUG if (tp == 0 || (tp->t_inpcb->inp_socket->so_options & SO_DEBUG)) tcp_trace(TA_DROP, ostate, tp, (void *)tcp_saveipgen, &tcp_savetcp, 0); #endif m_freem(m); /* destroy temporarily created socket */ if (dropsocket) (void) soabort(so); return; } static void tcp_dooptions(tp, cp, cnt, th, to) struct tcpcb *tp; u_char *cp; int cnt; struct tcphdr *th; struct tcpopt *to; { u_short mss = 0; int opt, optlen; for (; cnt > 0; cnt -= optlen, cp += optlen) { opt = cp[0]; if (opt == TCPOPT_EOL) break; if (opt == TCPOPT_NOP) optlen = 1; else { optlen = cp[1]; if (optlen <= 0) break; } switch (opt) { default: continue; case TCPOPT_MAXSEG: if (optlen != TCPOLEN_MAXSEG) continue; if (!(th->th_flags & TH_SYN)) continue; bcopy((char *) cp + 2, (char *) &mss, sizeof(mss)); NTOHS(mss); break; case TCPOPT_WINDOW: if (optlen != TCPOLEN_WINDOW) continue; if (!(th->th_flags & TH_SYN)) continue; tp->t_flags |= TF_RCVD_SCALE; tp->requested_s_scale = min(cp[2], TCP_MAX_WINSHIFT); break; case TCPOPT_TIMESTAMP: if (optlen != TCPOLEN_TIMESTAMP) continue; to->to_flag |= TOF_TS; bcopy((char *)cp + 2, (char *)&to->to_tsval, sizeof(to->to_tsval)); NTOHL(to->to_tsval); bcopy((char *)cp + 6, (char *)&to->to_tsecr, sizeof(to->to_tsecr)); NTOHL(to->to_tsecr); /* * A timestamp received in a SYN makes * it ok to send timestamp requests and replies. */ if (th->th_flags & TH_SYN) { tp->t_flags |= TF_RCVD_TSTMP; tp->ts_recent = to->to_tsval; tp->ts_recent_age = ticks; } break; case TCPOPT_CC: if (optlen != TCPOLEN_CC) continue; to->to_flag |= TOF_CC; bcopy((char *)cp + 2, (char *)&to->to_cc, sizeof(to->to_cc)); NTOHL(to->to_cc); /* * A CC or CC.new option received in a SYN makes * it ok to send CC in subsequent segments. */ if (th->th_flags & TH_SYN) tp->t_flags |= TF_RCVD_CC; break; case TCPOPT_CCNEW: if (optlen != TCPOLEN_CC) continue; if (!(th->th_flags & TH_SYN)) continue; to->to_flag |= TOF_CCNEW; bcopy((char *)cp + 2, (char *)&to->to_cc, sizeof(to->to_cc)); NTOHL(to->to_cc); /* * A CC or CC.new option received in a SYN makes * it ok to send CC in subsequent segments. */ tp->t_flags |= TF_RCVD_CC; break; case TCPOPT_CCECHO: if (optlen != TCPOLEN_CC) continue; if (!(th->th_flags & TH_SYN)) continue; to->to_flag |= TOF_CCECHO; bcopy((char *)cp + 2, (char *)&to->to_ccecho, sizeof(to->to_ccecho)); NTOHL(to->to_ccecho); break; } } if (th->th_flags & TH_SYN) tcp_mss(tp, mss); /* sets t_maxseg */ } /* * Pull out of band byte out of a segment so * it doesn't appear in the user's data queue. * It is still reflected in the segment length for * sequencing purposes. */ static void tcp_pulloutofband(so, th, m, off) struct socket *so; struct tcphdr *th; register struct mbuf *m; int off; /* delayed to be droped hdrlen */ { int cnt = off + th->th_urp - 1; while (cnt >= 0) { if (m->m_len > cnt) { char *cp = mtod(m, caddr_t) + cnt; struct tcpcb *tp = sototcpcb(so); tp->t_iobc = *cp; tp->t_oobflags |= TCPOOB_HAVEDATA; bcopy(cp+1, cp, (unsigned)(m->m_len - cnt - 1)); m->m_len--; if (m->m_flags & M_PKTHDR) m->m_pkthdr.len--; return; } cnt -= m->m_len; m = m->m_next; if (m == 0) break; } panic("tcp_pulloutofband"); } /* * Collect new round-trip time estimate * and update averages and current timeout. */ static void tcp_xmit_timer(tp, rtt) register struct tcpcb *tp; int rtt; { register int delta; tcpstat.tcps_rttupdated++; tp->t_rttupdated++; if (tp->t_srtt != 0) { /* * srtt is stored as fixed point with 5 bits after the * binary point (i.e., scaled by 8). The following magic * is equivalent to the smoothing algorithm in rfc793 with * an alpha of .875 (srtt = rtt/8 + srtt*7/8 in fixed * point). Adjust rtt to origin 0. */ delta = ((rtt - 1) << TCP_DELTA_SHIFT) - (tp->t_srtt >> (TCP_RTT_SHIFT - TCP_DELTA_SHIFT)); if ((tp->t_srtt += delta) <= 0) tp->t_srtt = 1; /* * We accumulate a smoothed rtt variance (actually, a * smoothed mean difference), then set the retransmit * timer to smoothed rtt + 4 times the smoothed variance. * rttvar is stored as fixed point with 4 bits after the * binary point (scaled by 16). The following is * equivalent to rfc793 smoothing with an alpha of .75 * (rttvar = rttvar*3/4 + |delta| / 4). This replaces * rfc793's wired-in beta. */ if (delta < 0) delta = -delta; delta -= tp->t_rttvar >> (TCP_RTTVAR_SHIFT - TCP_DELTA_SHIFT); if ((tp->t_rttvar += delta) <= 0) tp->t_rttvar = 1; } else { /* * No rtt measurement yet - use the unsmoothed rtt. * Set the variance to half the rtt (so our first * retransmit happens at 3*rtt). */ tp->t_srtt = rtt << TCP_RTT_SHIFT; tp->t_rttvar = rtt << (TCP_RTTVAR_SHIFT - 1); } tp->t_rtttime = 0; tp->t_rxtshift = 0; /* * the retransmit should happen at rtt + 4 * rttvar. * Because of the way we do the smoothing, srtt and rttvar * will each average +1/2 tick of bias. When we compute * the retransmit timer, we want 1/2 tick of rounding and * 1 extra tick because of +-1/2 tick uncertainty in the * firing of the timer. The bias will give us exactly the * 1.5 tick we need. But, because the bias is * statistical, we have to test that we don't drop below * the minimum feasible timer (which is 2 ticks). */ TCPT_RANGESET(tp->t_rxtcur, TCP_REXMTVAL(tp), max(tp->t_rttmin, rtt + 2), TCPTV_REXMTMAX); /* * We received an ack for a packet that wasn't retransmitted; * it is probably safe to discard any error indications we've * received recently. This isn't quite right, but close enough * for now (a route might have failed after we sent a segment, * and the return path might not be symmetrical). */ tp->t_softerror = 0; } /* * Determine a reasonable value for maxseg size. * If the route is known, check route for mtu. * If none, use an mss that can be handled on the outgoing * interface without forcing IP to fragment; if bigger than * an mbuf cluster (MCLBYTES), round down to nearest multiple of MCLBYTES * to utilize large mbufs. If no route is found, route has no mtu, * or the destination isn't local, use a default, hopefully conservative * size (usually 512 or the default IP max size, but no more than the mtu * of the interface), as we can't discover anything about intervening * gateways or networks. We also initialize the congestion/slow start * window to be a single segment if the destination isn't local. * While looking at the routing entry, we also initialize other path-dependent * parameters from pre-set or cached values in the routing entry. * * Also take into account the space needed for options that we * send regularly. Make maxseg shorter by that amount to assure * that we can send maxseg amount of data even when the options * are present. Store the upper limit of the length of options plus * data in maxopd. * * NOTE that this routine is only called when we process an incoming * segment, for outgoing segments only tcp_mssopt is called. * * In case of T/TCP, we call this routine during implicit connection * setup as well (offer = -1), to initialize maxseg from the cached * MSS of our peer. */ void tcp_mss(tp, offer) struct tcpcb *tp; int offer; { register struct rtentry *rt; struct ifnet *ifp; register int rtt, mss; u_long bufsize; struct inpcb *inp; struct socket *so; struct rmxp_tao *taop; int origoffer = offer; #ifdef INET6 int isipv6; int min_protoh; #endif inp = tp->t_inpcb; #ifdef INET6 isipv6 = ((inp->inp_vflag & INP_IPV6) != 0) ? 1 : 0; min_protoh = isipv6 ? sizeof (struct ip6_hdr) + sizeof (struct tcphdr) : sizeof (struct tcpiphdr); #else #define min_protoh (sizeof (struct tcpiphdr)) #endif #ifdef INET6 if (isipv6) rt = tcp_rtlookup6(inp); else #endif rt = tcp_rtlookup(inp); if (rt == NULL) { tp->t_maxopd = tp->t_maxseg = #ifdef INET6 isipv6 ? tcp_v6mssdflt : #endif /* INET6 */ tcp_mssdflt; return; } ifp = rt->rt_ifp; so = inp->inp_socket; taop = rmx_taop(rt->rt_rmx); /* * Offer == -1 means that we didn't receive SYN yet, * use cached value in that case; */ if (offer == -1) offer = taop->tao_mssopt; /* * Offer == 0 means that there was no MSS on the SYN segment, * in this case we use tcp_mssdflt. */ if (offer == 0) offer = #ifdef INET6 isipv6 ? tcp_v6mssdflt : #endif /* INET6 */ tcp_mssdflt; else /* * Sanity check: make sure that maxopd will be large * enough to allow some data on segments even is the * all the option space is used (40bytes). Otherwise * funny things may happen in tcp_output. */ offer = max(offer, 64); taop->tao_mssopt = offer; /* * While we're here, check if there's an initial rtt * or rttvar. Convert from the route-table units * to scaled multiples of the slow timeout timer. */ if (tp->t_srtt == 0 && (rtt = rt->rt_rmx.rmx_rtt)) { /* * XXX the lock bit for RTT indicates that the value * is also a minimum value; this is subject to time. */ if (rt->rt_rmx.rmx_locks & RTV_RTT) tp->t_rttmin = rtt / (RTM_RTTUNIT / hz); tp->t_srtt = rtt / (RTM_RTTUNIT / (hz * TCP_RTT_SCALE)); tcpstat.tcps_usedrtt++; if (rt->rt_rmx.rmx_rttvar) { tp->t_rttvar = rt->rt_rmx.rmx_rttvar / (RTM_RTTUNIT / (hz * TCP_RTTVAR_SCALE)); tcpstat.tcps_usedrttvar++; } else { /* default variation is +- 1 rtt */ tp->t_rttvar = tp->t_srtt * TCP_RTTVAR_SCALE / TCP_RTT_SCALE; } TCPT_RANGESET(tp->t_rxtcur, ((tp->t_srtt >> 2) + tp->t_rttvar) >> 1, tp->t_rttmin, TCPTV_REXMTMAX); } /* * if there's an mtu associated with the route, use it * else, use the link mtu. */ if (rt->rt_rmx.rmx_mtu) mss = rt->rt_rmx.rmx_mtu - min_protoh; else { mss = #ifdef INET6 (isipv6 ? nd_ifinfo[rt->rt_ifp->if_index].linkmtu : #endif ifp->if_mtu #ifdef INET6 ) #endif - min_protoh; #ifdef INET6 if (isipv6) { if (!in6_localaddr(&inp->in6p_faddr)) mss = min(mss, tcp_v6mssdflt); } else #endif if (!in_localaddr(inp->inp_faddr)) mss = min(mss, tcp_mssdflt); } mss = min(mss, offer); /* * maxopd stores the maximum length of data AND options * in a segment; maxseg is the amount of data in a normal * segment. We need to store this value (maxopd) apart * from maxseg, because now every segment carries options * and thus we normally have somewhat less data in segments. */ tp->t_maxopd = mss; /* * In case of T/TCP, origoffer==-1 indicates, that no segments * were received yet. In this case we just guess, otherwise * we do the same as before T/TCP. */ if ((tp->t_flags & (TF_REQ_TSTMP|TF_NOOPT)) == TF_REQ_TSTMP && (origoffer == -1 || (tp->t_flags & TF_RCVD_TSTMP) == TF_RCVD_TSTMP)) mss -= TCPOLEN_TSTAMP_APPA; if ((tp->t_flags & (TF_REQ_CC|TF_NOOPT)) == TF_REQ_CC && (origoffer == -1 || (tp->t_flags & TF_RCVD_CC) == TF_RCVD_CC)) mss -= TCPOLEN_CC_APPA; #if (MCLBYTES & (MCLBYTES - 1)) == 0 if (mss > MCLBYTES) mss &= ~(MCLBYTES-1); #else if (mss > MCLBYTES) mss = mss / MCLBYTES * MCLBYTES; #endif /* * If there's a pipesize, change the socket buffer * to that size. Make the socket buffers an integral * number of mss units; if the mss is larger than * the socket buffer, decrease the mss. */ #ifdef RTV_SPIPE if ((bufsize = rt->rt_rmx.rmx_sendpipe) == 0) #endif bufsize = so->so_snd.sb_hiwat; if (bufsize < mss) mss = bufsize; else { bufsize = roundup(bufsize, mss); if (bufsize > sb_max) bufsize = sb_max; (void)sbreserve(&so->so_snd, bufsize, so, NULL); } tp->t_maxseg = mss; #ifdef RTV_RPIPE if ((bufsize = rt->rt_rmx.rmx_recvpipe) == 0) #endif bufsize = so->so_rcv.sb_hiwat; if (bufsize > mss) { bufsize = roundup(bufsize, mss); if (bufsize > sb_max) bufsize = sb_max; (void)sbreserve(&so->so_rcv, bufsize, so, NULL); } /* * Set the slow-start flight size depending on whether this * is a local network or not. */ if ( #ifdef INET6 (isipv6 && in6_localaddr(&inp->in6p_faddr)) || (!isipv6 && #endif in_localaddr(inp->inp_faddr) #ifdef INET6 ) #endif ) tp->snd_cwnd = mss * ss_fltsz_local; else tp->snd_cwnd = mss * ss_fltsz; if (rt->rt_rmx.rmx_ssthresh) { /* * There's some sort of gateway or interface * buffer limit on the path. Use this to set * the slow start threshhold, but set the * threshold to no less than 2*mss. */ tp->snd_ssthresh = max(2 * mss, rt->rt_rmx.rmx_ssthresh); tcpstat.tcps_usedssthresh++; } } /* * Determine the MSS option to send on an outgoing SYN. */ int tcp_mssopt(tp) struct tcpcb *tp; { struct rtentry *rt; #ifdef INET6 int isipv6; int min_protoh; #endif #ifdef INET6 isipv6 = ((tp->t_inpcb->inp_vflag & INP_IPV6) != 0) ? 1 : 0; min_protoh = isipv6 ? sizeof (struct ip6_hdr) + sizeof (struct tcphdr) : sizeof (struct tcpiphdr); #else #define min_protoh (sizeof (struct tcpiphdr)) #endif #ifdef INET6 if (isipv6) rt = tcp_rtlookup6(tp->t_inpcb); else #endif /* INET6 */ rt = tcp_rtlookup(tp->t_inpcb); if (rt == NULL) return #ifdef INET6 isipv6 ? tcp_v6mssdflt : #endif /* INET6 */ tcp_mssdflt; return rt->rt_ifp->if_mtu - min_protoh; } /* * Checks for partial ack. If partial ack arrives, force the retransmission * of the next unacknowledged segment, do not clear tp->t_dupacks, and return * 1. By setting snd_nxt to ti_ack, this forces retransmission timer to * be started again. If the ack advances at least to tp->snd_recover, return 0. */ static int tcp_newreno(tp, th) struct tcpcb *tp; struct tcphdr *th; { if (SEQ_LT(th->th_ack, tp->snd_recover)) { tcp_seq onxt = tp->snd_nxt; u_long ocwnd = tp->snd_cwnd; callout_stop(tp->tt_rexmt); tp->t_rtttime = 0; tp->snd_nxt = th->th_ack; /* * Set snd_cwnd to one segment beyond acknowledged offset * (tp->snd_una has not yet been updated when this function * is called) */ tp->snd_cwnd = tp->t_maxseg + (th->th_ack - tp->snd_una); (void) tcp_output(tp); tp->snd_cwnd = ocwnd; if (SEQ_GT(onxt, tp->snd_nxt)) tp->snd_nxt = onxt; /* * Partial window deflation. Relies on fact that tp->snd_una * not updated yet. */ tp->snd_cwnd -= (th->th_ack - tp->snd_una - tp->t_maxseg); return (1); } return (0); } Index: head/sys/netinet/tcp_output.c =================================================================== --- head/sys/netinet/tcp_output.c (revision 62586) +++ head/sys/netinet/tcp_output.c (revision 62587) @@ -1,922 +1,929 @@ /* * Copyright (c) 1982, 1986, 1988, 1990, 1993, 1995 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)tcp_output.c 8.4 (Berkeley) 5/24/95 * $FreeBSD$ */ #include "opt_inet6.h" #include "opt_ipsec.h" #include "opt_tcpdebug.h" #include #include #include #include #include #include +#include #include #include #include #include #include #include #include -#ifdef INET6 -#include -#endif #include -#ifdef INET6 -#include -#endif #include #ifdef INET6 +#include +#include #include #endif #include #define TCPOUTFLAGS #include #include #include #include #include #ifdef TCPDEBUG #include #endif #ifdef IPSEC #include #endif /*IPSEC*/ #include #ifdef notyet extern struct mbuf *m_copypack(); #endif static int path_mtu_discovery = 1; SYSCTL_INT(_net_inet_tcp, OID_AUTO, path_mtu_discovery, CTLFLAG_RW, &path_mtu_discovery, 1, "Enable Path MTU Discovery"); int ss_fltsz = 1; SYSCTL_INT(_net_inet_tcp, OID_AUTO, slowstart_flightsize, CTLFLAG_RW, &ss_fltsz, 1, "Slow start flight size"); int ss_fltsz_local = TCP_MAXWIN; /* something large */ SYSCTL_INT(_net_inet_tcp, OID_AUTO, local_slowstart_flightsize, CTLFLAG_RW, &ss_fltsz_local, 1, "Slow start flight size for local networks"); int tcp_do_newreno = 0; SYSCTL_INT(_net_inet_tcp, OID_AUTO, newreno, CTLFLAG_RW, &tcp_do_newreno, 0, "Enable NewReno Algorithms"); /* * Tcp output routine: figure out what should be sent and send it. */ int tcp_output(tp) register struct tcpcb *tp; { register struct socket *so = tp->t_inpcb->inp_socket; register long len, win; int off, flags, error; register struct mbuf *m; struct ip *ip = NULL; register struct ipovly *ipov = NULL; #ifdef INET6 struct ip6_hdr *ip6 = NULL; #endif /* INET6 */ register struct tcphdr *th; u_char opt[TCP_MAXOLEN]; unsigned ipoptlen, optlen, hdrlen; int idle, sendalot; int maxburst = TCP_MAXBURST; struct rmxp_tao *taop; struct rmxp_tao tao_noncached; #ifdef INET6 int isipv6; #endif #ifdef INET6 isipv6 = (tp->t_inpcb->inp_vflag & INP_IPV6) != 0; #endif /* * Determine length of data that should be transmitted, * and flags that will be used. * If there is some data or critical controls (SYN, RST) * to send, then transmit; otherwise, investigate further. */ idle = (tp->snd_max == tp->snd_una); if (idle && (ticks - tp->t_rcvtime) >= tp->t_rxtcur) { /* * We have been idle for "a while" and no acks are * expected to clock out any data we send -- * slow start to get ack "clock" running again. * * Set the slow-start flight size depending on whether * this is a local network or not. */ if ( #ifdef INET6 (isipv6 && in6_localaddr(&tp->t_inpcb->in6p_faddr)) || (!isipv6 && #endif in_localaddr(tp->t_inpcb->inp_faddr) #ifdef INET6 ) #endif ) tp->snd_cwnd = tp->t_maxseg * ss_fltsz_local; else tp->snd_cwnd = tp->t_maxseg * ss_fltsz; } again: sendalot = 0; off = tp->snd_nxt - tp->snd_una; win = min(tp->snd_wnd, tp->snd_cwnd); flags = tcp_outflags[tp->t_state]; /* * Get standard flags, and add SYN or FIN if requested by 'hidden' * state flags. */ if (tp->t_flags & TF_NEEDFIN) flags |= TH_FIN; if (tp->t_flags & TF_NEEDSYN) flags |= TH_SYN; /* * If in persist timeout with window of 0, send 1 byte. * Otherwise, if window is small but nonzero * and timer expired, we will send what we can * and go to transmit state. */ if (tp->t_force) { if (win == 0) { /* * If we still have some data to send, then * clear the FIN bit. Usually this would * happen below when it realizes that we * aren't sending all the data. However, * if we have exactly 1 byte of unsent data, * then it won't clear the FIN bit below, * and if we are in persist state, we wind * up sending the packet without recording * that we sent the FIN bit. * * We can't just blindly clear the FIN bit, * because if we don't have any more data * to send then the probe will be the FIN * itself. */ if (off < so->so_snd.sb_cc) flags &= ~TH_FIN; win = 1; } else { callout_stop(tp->tt_persist); tp->t_rxtshift = 0; } } len = (long)ulmin(so->so_snd.sb_cc, win) - off; if ((taop = tcp_gettaocache(tp->t_inpcb)) == NULL) { taop = &tao_noncached; bzero(taop, sizeof(*taop)); } /* * Lop off SYN bit if it has already been sent. However, if this * is SYN-SENT state and if segment contains data and if we don't * know that foreign host supports TAO, suppress sending segment. */ if ((flags & TH_SYN) && SEQ_GT(tp->snd_nxt, tp->snd_una)) { flags &= ~TH_SYN; off--, len++; if (len > 0 && tp->t_state == TCPS_SYN_SENT && taop->tao_ccsent == 0) return 0; } /* * Be careful not to send data and/or FIN on SYN segments * in cases when no CC option will be sent. * This measure is needed to prevent interoperability problems * with not fully conformant TCP implementations. */ if ((flags & TH_SYN) && ((tp->t_flags & TF_NOOPT) || !(tp->t_flags & TF_REQ_CC) || ((flags & TH_ACK) && !(tp->t_flags & TF_RCVD_CC)))) { len = 0; flags &= ~TH_FIN; } if (len < 0) { /* * If FIN has been sent but not acked, * but we haven't been called to retransmit, * len will be -1. Otherwise, window shrank * after we sent into it. If window shrank to 0, * cancel pending retransmit, pull snd_nxt back * to (closed) window, and set the persist timer * if it isn't already going. If the window didn't * close completely, just wait for an ACK. */ len = 0; if (win == 0) { callout_stop(tp->tt_rexmt); tp->t_rxtshift = 0; tp->snd_nxt = tp->snd_una; if (!callout_active(tp->tt_persist)) tcp_setpersist(tp); } } if (len > tp->t_maxseg) { len = tp->t_maxseg; sendalot = 1; } if (SEQ_LT(tp->snd_nxt + len, tp->snd_una + so->so_snd.sb_cc)) flags &= ~TH_FIN; win = sbspace(&so->so_rcv); /* * Sender silly window avoidance. If connection is idle * and can send all data, a maximum segment, * at least a maximum default-size segment do it, * or are forced, do it; otherwise don't bother. * If peer's buffer is tiny, then send * when window is at least half open. * If retransmitting (possibly after persist timer forced us * to send into a small window), then must resend. */ if (len) { if (len == tp->t_maxseg) goto send; if (!(tp->t_flags & TF_MORETOCOME) && (idle || tp->t_flags & TF_NODELAY) && (tp->t_flags & TF_NOPUSH) == 0 && len + off >= so->so_snd.sb_cc) goto send; if (tp->t_force) goto send; if (len >= tp->max_sndwnd / 2 && tp->max_sndwnd > 0) goto send; if (SEQ_LT(tp->snd_nxt, tp->snd_max)) goto send; } /* * Compare available window to amount of window * known to peer (as advertised window less * next expected input). If the difference is at least two * max size segments, or at least 50% of the maximum possible * window, then want to send a window update to peer. */ if (win > 0) { /* * "adv" is the amount we can increase the window, * taking into account that we are limited by * TCP_MAXWIN << tp->rcv_scale. */ long adv = min(win, (long)TCP_MAXWIN << tp->rcv_scale) - (tp->rcv_adv - tp->rcv_nxt); if (adv >= (long) (2 * tp->t_maxseg)) goto send; if (2 * adv >= (long) so->so_rcv.sb_hiwat) goto send; } /* * Send if we owe peer an ACK. */ if (tp->t_flags & TF_ACKNOW) goto send; if ((flags & TH_RST) || ((flags & TH_SYN) && (tp->t_flags & TF_NEEDSYN) == 0)) goto send; if (SEQ_GT(tp->snd_up, tp->snd_una)) goto send; /* * If our state indicates that FIN should be sent * and we have not yet done so, or we're retransmitting the FIN, * then we need to send. */ if (flags & TH_FIN && ((tp->t_flags & TF_SENTFIN) == 0 || tp->snd_nxt == tp->snd_una)) goto send; /* * TCP window updates are not reliable, rather a polling protocol * using ``persist'' packets is used to insure receipt of window * updates. The three ``states'' for the output side are: * idle not doing retransmits or persists * persisting to move a small or zero window * (re)transmitting and thereby not persisting * * callout_active(tp->tt_persist) * is true when we are in persist state. * tp->t_force * is set when we are called to send a persist packet. * callout_active(tp->tt_rexmt) * is set when we are retransmitting * The output side is idle when both timers are zero. * * If send window is too small, there is data to transmit, and no * retransmit or persist is pending, then go to persist state. * If nothing happens soon, send when timer expires: * if window is nonzero, transmit what we can, * otherwise force out a byte. */ if (so->so_snd.sb_cc && !callout_active(tp->tt_rexmt) && !callout_active(tp->tt_persist)) { tp->t_rxtshift = 0; tcp_setpersist(tp); } /* * No reason to send a segment, just return. */ return (0); send: /* * Before ESTABLISHED, force sending of initial options * unless TCP set not to do any options. * NOTE: we assume that the IP/TCP header plus TCP options * always fit in a single mbuf, leaving room for a maximum * link header, i.e. * max_linkhdr + sizeof (struct tcpiphdr) + optlen <= MHLEN */ optlen = 0; #ifdef INET6 if (isipv6) hdrlen = sizeof (struct ip6_hdr) + sizeof (struct tcphdr); else #endif hdrlen = sizeof (struct tcpiphdr); if (flags & TH_SYN) { tp->snd_nxt = tp->iss; if ((tp->t_flags & TF_NOOPT) == 0) { u_short mss; opt[0] = TCPOPT_MAXSEG; opt[1] = TCPOLEN_MAXSEG; mss = htons((u_short) tcp_mssopt(tp)); (void)memcpy(opt + 2, &mss, sizeof(mss)); optlen = TCPOLEN_MAXSEG; if ((tp->t_flags & TF_REQ_SCALE) && ((flags & TH_ACK) == 0 || (tp->t_flags & TF_RCVD_SCALE))) { *((u_int32_t *)(opt + optlen)) = htonl( TCPOPT_NOP << 24 | TCPOPT_WINDOW << 16 | TCPOLEN_WINDOW << 8 | tp->request_r_scale); optlen += 4; } } } /* * Send a timestamp and echo-reply if this is a SYN and our side * wants to use timestamps (TF_REQ_TSTMP is set) or both our side * and our peer have sent timestamps in our SYN's. */ if ((tp->t_flags & (TF_REQ_TSTMP|TF_NOOPT)) == TF_REQ_TSTMP && (flags & TH_RST) == 0 && ((flags & TH_ACK) == 0 || (tp->t_flags & TF_RCVD_TSTMP))) { u_int32_t *lp = (u_int32_t *)(opt + optlen); /* Form timestamp option as shown in appendix A of RFC 1323. */ *lp++ = htonl(TCPOPT_TSTAMP_HDR); *lp++ = htonl(ticks); *lp = htonl(tp->ts_recent); optlen += TCPOLEN_TSTAMP_APPA; } /* * Send `CC-family' options if our side wants to use them (TF_REQ_CC), * options are allowed (!TF_NOOPT) and it's not a RST. */ if ((tp->t_flags & (TF_REQ_CC|TF_NOOPT)) == TF_REQ_CC && (flags & TH_RST) == 0) { switch (flags & (TH_SYN|TH_ACK)) { /* * This is a normal ACK, send CC if we received CC before * from our peer. */ case TH_ACK: if (!(tp->t_flags & TF_RCVD_CC)) break; /*FALLTHROUGH*/ /* * We can only get here in T/TCP's SYN_SENT* state, when * we're a sending a non-SYN segment without waiting for * the ACK of our SYN. A check above assures that we only * do this if our peer understands T/TCP. */ case 0: opt[optlen++] = TCPOPT_NOP; opt[optlen++] = TCPOPT_NOP; opt[optlen++] = TCPOPT_CC; opt[optlen++] = TCPOLEN_CC; *(u_int32_t *)&opt[optlen] = htonl(tp->cc_send); optlen += 4; break; /* * This is our initial SYN, check whether we have to use * CC or CC.new. */ case TH_SYN: opt[optlen++] = TCPOPT_NOP; opt[optlen++] = TCPOPT_NOP; opt[optlen++] = tp->t_flags & TF_SENDCCNEW ? TCPOPT_CCNEW : TCPOPT_CC; opt[optlen++] = TCPOLEN_CC; *(u_int32_t *)&opt[optlen] = htonl(tp->cc_send); optlen += 4; break; /* * This is a SYN,ACK; send CC and CC.echo if we received * CC from our peer. */ case (TH_SYN|TH_ACK): if (tp->t_flags & TF_RCVD_CC) { opt[optlen++] = TCPOPT_NOP; opt[optlen++] = TCPOPT_NOP; opt[optlen++] = TCPOPT_CC; opt[optlen++] = TCPOLEN_CC; *(u_int32_t *)&opt[optlen] = htonl(tp->cc_send); optlen += 4; opt[optlen++] = TCPOPT_NOP; opt[optlen++] = TCPOPT_NOP; opt[optlen++] = TCPOPT_CCECHO; opt[optlen++] = TCPOLEN_CC; *(u_int32_t *)&opt[optlen] = htonl(tp->cc_recv); optlen += 4; } break; } } hdrlen += optlen; #ifdef INET6 if (isipv6) ipoptlen = ip6_optlen(tp->t_inpcb); else #endif { if (tp->t_inpcb->inp_options) { ipoptlen = tp->t_inpcb->inp_options->m_len - offsetof(struct ipoption, ipopt_list); } else { ipoptlen = 0; } } #ifdef IPSEC ipoptlen += ipsec_hdrsiz_tcp(tp); #endif /* * Adjust data length if insertion of options will * bump the packet length beyond the t_maxopd length. * Clear the FIN bit because we cut off the tail of * the segment. */ if (len + optlen + ipoptlen > tp->t_maxopd) { /* * If there is still more to send, don't close the connection. */ flags &= ~TH_FIN; len = tp->t_maxopd - optlen - ipoptlen; sendalot = 1; } /*#ifdef DIAGNOSTIC*/ #ifdef INET6 if (max_linkhdr + hdrlen > MCLBYTES) panic("tcphdr too big"); #else if (max_linkhdr + hdrlen > MHLEN) panic("tcphdr too big"); #endif /*#endif*/ /* * Grab a header mbuf, attaching a copy of data to * be transmitted, and initialize the header from * the template for sends on this connection. */ if (len) { if (tp->t_force && len == 1) tcpstat.tcps_sndprobe++; else if (SEQ_LT(tp->snd_nxt, tp->snd_max)) { tcpstat.tcps_sndrexmitpack++; tcpstat.tcps_sndrexmitbyte += len; } else { tcpstat.tcps_sndpack++; tcpstat.tcps_sndbyte += len; } #ifdef notyet if ((m = m_copypack(so->so_snd.sb_mb, off, (int)len, max_linkhdr + hdrlen)) == 0) { error = ENOBUFS; goto out; } /* * m_copypack left space for our hdr; use it. */ m->m_len += hdrlen; m->m_data -= hdrlen; #else MGETHDR(m, M_DONTWAIT, MT_HEADER); if (m == NULL) { error = ENOBUFS; goto out; } #ifdef INET6 if (MHLEN < hdrlen + max_linkhdr) { MCLGET(m, M_DONTWAIT); if ((m->m_flags & M_EXT) == 0) { m_freem(m); error = ENOBUFS; goto out; } } #endif m->m_data += max_linkhdr; m->m_len = hdrlen; if (len <= MHLEN - hdrlen - max_linkhdr) { m_copydata(so->so_snd.sb_mb, off, (int) len, mtod(m, caddr_t) + hdrlen); m->m_len += len; } else { m->m_next = m_copy(so->so_snd.sb_mb, off, (int) len); if (m->m_next == 0) { (void) m_free(m); error = ENOBUFS; goto out; } } #endif /* * If we're sending everything we've got, set PUSH. * (This will keep happy those implementations which only * give data to the user when a buffer fills or * a PUSH comes in.) */ if (off + len == so->so_snd.sb_cc) flags |= TH_PUSH; } else { if (tp->t_flags & TF_ACKNOW) tcpstat.tcps_sndacks++; else if (flags & (TH_SYN|TH_FIN|TH_RST)) tcpstat.tcps_sndctrl++; else if (SEQ_GT(tp->snd_up, tp->snd_una)) tcpstat.tcps_sndurg++; else tcpstat.tcps_sndwinup++; MGETHDR(m, M_DONTWAIT, MT_HEADER); if (m == NULL) { error = ENOBUFS; goto out; } #ifdef INET6 if (isipv6 && (MHLEN < hdrlen + max_linkhdr) && MHLEN >= hdrlen) { MH_ALIGN(m, hdrlen); } else #endif m->m_data += max_linkhdr; m->m_len = hdrlen; } m->m_pkthdr.rcvif = (struct ifnet *)0; if (tp->t_template == 0) panic("tcp_output"); #ifdef INET6 if (isipv6) { ip6 = mtod(m, struct ip6_hdr *); th = (struct tcphdr *)(ip6 + 1); bcopy((caddr_t)tp->t_template->tt_ipgen, (caddr_t)ip6, sizeof(struct ip6_hdr)); bcopy((caddr_t)&tp->t_template->tt_t, (caddr_t)th, sizeof(struct tcphdr)); } else #endif /* INET6 */ { ip = mtod(m, struct ip *); ipov = (struct ipovly *)ip; th = (struct tcphdr *)(ip + 1); /* this picks up the pseudo header (w/o the length) */ bcopy((caddr_t)tp->t_template->tt_ipgen, (caddr_t)ip, sizeof(struct ip)); bcopy((caddr_t)&tp->t_template->tt_t, (caddr_t)th, sizeof(struct tcphdr)); } /* * Fill in fields, remembering maximum advertised * window for use in delaying messages about window sizes. * If resending a FIN, be sure not to use a new sequence number. */ if (flags & TH_FIN && tp->t_flags & TF_SENTFIN && tp->snd_nxt == tp->snd_max) tp->snd_nxt--; /* * If we are doing retransmissions, then snd_nxt will * not reflect the first unsent octet. For ACK only * packets, we do not want the sequence number of the * retransmitted packet, we want the sequence number * of the next unsent octet. So, if there is no data * (and no SYN or FIN), use snd_max instead of snd_nxt * when filling in ti_seq. But if we are in persist * state, snd_max might reflect one byte beyond the * right edge of the window, so use snd_nxt in that * case, since we know we aren't doing a retransmission. * (retransmit and persist are mutually exclusive...) */ if (len || (flags & (TH_SYN|TH_FIN)) || callout_active(tp->tt_persist)) th->th_seq = htonl(tp->snd_nxt); else th->th_seq = htonl(tp->snd_max); th->th_ack = htonl(tp->rcv_nxt); if (optlen) { bcopy(opt, th + 1, optlen); th->th_off = (sizeof (struct tcphdr) + optlen) >> 2; } th->th_flags = flags; /* * Calculate receive window. Don't shrink window, * but avoid silly window syndrome. */ if (win < (long)(so->so_rcv.sb_hiwat / 4) && win < (long)tp->t_maxseg) win = 0; if (win < (long)(tp->rcv_adv - tp->rcv_nxt)) win = (long)(tp->rcv_adv - tp->rcv_nxt); if (win > (long)TCP_MAXWIN << tp->rcv_scale) win = (long)TCP_MAXWIN << tp->rcv_scale; th->th_win = htons((u_short) (win>>tp->rcv_scale)); if (SEQ_GT(tp->snd_up, tp->snd_nxt)) { th->th_urp = htons((u_short)(tp->snd_up - tp->snd_nxt)); th->th_flags |= TH_URG; } else /* * If no urgent pointer to send, then we pull * the urgent pointer to the left edge of the send window * so that it doesn't drift into the send window on sequence * number wraparound. */ tp->snd_up = tp->snd_una; /* drag it along */ /* * Put TCP length in extended header, and then * checksum extended header and data. */ m->m_pkthdr.len = hdrlen + len; /* in6_cksum() need this */ #ifdef INET6 if (isipv6) /* * ip6_plen is not need to be filled now, and will be filled * in ip6_output. */ th->th_sum = in6_cksum(m, IPPROTO_TCP, sizeof(struct ip6_hdr), sizeof(struct tcphdr) + optlen + len); else #endif /* INET6 */ { m->m_pkthdr.csum_flags = CSUM_TCP; m->m_pkthdr.csum_data = offsetof(struct tcphdr, th_sum); if (len + optlen) th->th_sum = in_addword(th->th_sum, htons((u_short)(optlen + len))); /* IP version must be set here for ipv4/ipv6 checking later */ KASSERT(ip->ip_v == IPVERSION, ("%s: IP version incorrect: %d", __FUNCTION__, ip->ip_v)); } /* * In transmit state, time the transmission and arrange for * the retransmit. In persist state, just set snd_max. */ if (tp->t_force == 0 || !callout_active(tp->tt_persist)) { tcp_seq startseq = tp->snd_nxt; /* * Advance snd_nxt over sequence space of this segment. */ if (flags & (TH_SYN|TH_FIN)) { if (flags & TH_SYN) tp->snd_nxt++; if (flags & TH_FIN) { tp->snd_nxt++; tp->t_flags |= TF_SENTFIN; } } tp->snd_nxt += len; if (SEQ_GT(tp->snd_nxt, tp->snd_max)) { tp->snd_max = tp->snd_nxt; /* * Time this transmission if not a retransmission and * not currently timing anything. */ if (tp->t_rtttime == 0) { tp->t_rtttime = ticks; tp->t_rtseq = startseq; tcpstat.tcps_segstimed++; } } /* * Set retransmit timer if not currently set, * and not doing an ack or a keep-alive probe. * Initial value for retransmit timer is smoothed * round-trip time + 2 * round-trip time variance. * Initialize shift counter which is used for backoff * of retransmit time. */ if (!callout_active(tp->tt_rexmt) && tp->snd_nxt != tp->snd_una) { if (callout_active(tp->tt_persist)) { callout_stop(tp->tt_persist); tp->t_rxtshift = 0; } callout_reset(tp->tt_rexmt, tp->t_rxtcur, tcp_timer_rexmt, tp); } } else if (SEQ_GT(tp->snd_nxt + len, tp->snd_max)) tp->snd_max = tp->snd_nxt + len; #ifdef TCPDEBUG /* * Trace. */ if (so->so_options & SO_DEBUG) tcp_trace(TA_OUTPUT, tp->t_state, tp, mtod(m, void *), th, 0); #endif /* * Fill in IP length and desired time to live and * send to IP level. There should be a better way * to handle ttl and tos; we could keep them in * the template, but need a way to checksum without them. */ /* * m->m_pkthdr.len should have been set before cksum calcuration, * because in6_cksum() need it. */ #ifdef INET6 if (isipv6) { - /* + /* * we separately set hoplimit for every segment, since the * user might want to change the value via setsockopt. * Also, desired default hop limit might be changed via - * Neighbor Discovery. - */ - ip6->ip6_hlim = in6_selecthlim(tp->t_inpcb, - tp->t_inpcb->in6p_route.ro_rt ? - tp->t_inpcb->in6p_route.ro_rt->rt_ifp - : NULL); + * Neighbor Discovery. + */ + ip6->ip6_hlim = in6_selecthlim(tp->t_inpcb, + tp->t_inpcb->in6p_route.ro_rt ? + tp->t_inpcb->in6p_route.ro_rt->rt_ifp + : NULL); /* TODO: IPv6 IP6TOS_ECT bit on */ #ifdef IPSEC - m->m_pkthdr.rcvif = (struct ifnet *)so; + ipsec_setsocket(m, so); #endif /*IPSEC*/ error = ip6_output(m, tp->t_inpcb->in6p_outputopts, &tp->t_inpcb->in6p_route, - (so->so_options & SO_DONTROUTE)|IPV6_SOCKINMRCVIF, - NULL, NULL); + (so->so_options & SO_DONTROUTE), NULL, NULL); } else #endif /* INET6 */ { struct rtentry *rt; ip->ip_len = m->m_pkthdr.len; +#ifdef INET6 + if (INP_CHECK_SOCKAF(so, AF_INET6)) + ip->ip_ttl = in6_selecthlim(tp->t_inpcb, + tp->t_inpcb->in6p_route.ro_rt ? + tp->t_inpcb->in6p_route.ro_rt->rt_ifp + : NULL); + else +#endif /* INET6 */ ip->ip_ttl = tp->t_inpcb->inp_ip_ttl; /* XXX */ ip->ip_tos = tp->t_inpcb->inp_ip_tos; /* XXX */ /* * See if we should do MTU discovery. We do it only if the following * are true: * 1) we have a valid route to the destination * 2) the MTU is not locked (if it is, then discovery has been * disabled) */ if (path_mtu_discovery && (rt = tp->t_inpcb->inp_route.ro_rt) && rt->rt_flags & RTF_UP && !(rt->rt_rmx.rmx_locks & RTV_MTU)) { ip->ip_off |= IP_DF; } +#ifdef IPSEC + ipsec_setsocket(m, so); +#endif /*IPSEC*/ error = ip_output(m, tp->t_inpcb->inp_options, &tp->t_inpcb->inp_route, - (so->so_options & SO_DONTROUTE)|IP_SOCKINMRCVIF, 0); + (so->so_options & SO_DONTROUTE), 0); } if (error) { out: if (error == ENOBUFS) { if (!callout_active(tp->tt_rexmt) && !callout_active(tp->tt_persist)) callout_reset(tp->tt_rexmt, tp->t_rxtcur, tcp_timer_rexmt, tp); tcp_quench(tp->t_inpcb, 0); return (0); } if (error == EMSGSIZE) { /* * ip_output() will have already fixed the route * for us. tcp_mtudisc() will, as its last action, * initiate retransmission, so it is important to * not do so here. */ tcp_mtudisc(tp->t_inpcb, 0); return 0; } if ((error == EHOSTUNREACH || error == ENETDOWN) && TCPS_HAVERCVDSYN(tp->t_state)) { tp->t_softerror = error; return (0); } return (error); } tcpstat.tcps_sndtotal++; /* * Data sent (as far as we can tell). * If this advertises a larger window than any other segment, * then remember the size of the advertised window. * Any pending ACK has now been sent. */ if (win > 0 && SEQ_GT(tp->rcv_nxt+win, tp->rcv_adv)) tp->rcv_adv = tp->rcv_nxt + win; tp->last_ack_sent = tp->rcv_nxt; tp->t_flags &= ~TF_ACKNOW; if (tcp_delack_enabled) callout_stop(tp->tt_delack); if (sendalot && (!tcp_do_newreno || --maxburst)) goto again; return (0); } void tcp_setpersist(tp) register struct tcpcb *tp; { int t = ((tp->t_srtt >> 2) + tp->t_rttvar) >> 1; int tt; if (callout_active(tp->tt_rexmt)) panic("tcp_setpersist: retransmit pending"); /* * Start/restart persistance timer. */ TCPT_RANGESET(tt, t * tcp_backoff[tp->t_rxtshift], TCPTV_PERSMIN, TCPTV_PERSMAX); callout_reset(tp->tt_persist, tt, tcp_timer_persist, tp); if (tp->t_rxtshift < TCP_MAXRXTSHIFT) tp->t_rxtshift++; } Index: head/sys/netinet/tcp_reass.c =================================================================== --- head/sys/netinet/tcp_reass.c (revision 62586) +++ head/sys/netinet/tcp_reass.c (revision 62587) @@ -1,2859 +1,2855 @@ /* * Copyright (c) 1982, 1986, 1988, 1990, 1993, 1994, 1995 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)tcp_input.c 8.12 (Berkeley) 5/24/95 * $FreeBSD$ */ #include "opt_ipfw.h" /* for ipfw_fwd */ #include "opt_inet6.h" #include "opt_ipsec.h" #include "opt_tcpdebug.h" #include "opt_tcp_input.h" #include #include #include #include #include #include #include /* for proc0 declaration */ #include #include #include #include #include /* before tcp_seq.h, for tcp_random18() */ #include #include #include #include #include -#include -#ifdef INET6 -#include +#include /* for ICMP_BANDLIM */ #include -#include -#include -#endif +#include /* for ICMP_BANDLIM */ #include -#ifdef INET6 -#include -#endif #include #ifdef INET6 +#include +#include +#include #include +#include #endif -#include #include #include #include #include #include #ifdef INET6 #include #endif #include #ifdef TCPDEBUG #include u_char tcp_saveipgen[40]; /* the size must be of max ip header, now IPv6 */ struct tcphdr tcp_savetcp; #endif /* TCPDEBUG */ #ifdef IPSEC #include #ifdef INET6 #include #endif #include #endif /*IPSEC*/ #include MALLOC_DEFINE(M_TSEGQ, "tseg_qent", "TCP segment queue entry"); static int tcprexmtthresh = 3; tcp_seq tcp_iss; tcp_cc tcp_ccgen; struct tcpstat tcpstat; SYSCTL_STRUCT(_net_inet_tcp, TCPCTL_STATS, stats, CTLFLAG_RD, &tcpstat , tcpstat, "TCP statistics (struct tcpstat, netinet/tcp_var.h)"); static int log_in_vain = 0; SYSCTL_INT(_net_inet_tcp, OID_AUTO, log_in_vain, CTLFLAG_RW, &log_in_vain, 0, "Log all incoming TCP connections"); static int blackhole = 0; SYSCTL_INT(_net_inet_tcp, OID_AUTO, blackhole, CTLFLAG_RW, &blackhole, 0, "Do not send RST when dropping refused connections"); int tcp_delack_enabled = 1; SYSCTL_INT(_net_inet_tcp, OID_AUTO, delayed_ack, CTLFLAG_RW, &tcp_delack_enabled, 0, "Delay ACK to try and piggyback it onto a data packet"); #ifdef TCP_DROP_SYNFIN static int drop_synfin = 0; SYSCTL_INT(_net_inet_tcp, OID_AUTO, drop_synfin, CTLFLAG_RW, &drop_synfin, 0, "Drop TCP packets with SYN+FIN set"); #endif #ifdef TCP_RESTRICT_RST static int restrict_rst = 0; SYSCTL_INT(_net_inet_tcp, OID_AUTO, restrict_rst, CTLFLAG_RW, &restrict_rst, 0, "Restrict RST emission"); #endif struct inpcbhead tcb; #define tcb6 tcb /* for KAME src sync over BSD*'s */ struct inpcbinfo tcbinfo; static void tcp_dooptions __P((struct tcpcb *, u_char *, int, struct tcphdr *, struct tcpopt *)); static void tcp_pulloutofband __P((struct socket *, struct tcphdr *, struct mbuf *, int)); static int tcp_reass __P((struct tcpcb *, struct tcphdr *, int *, struct mbuf *)); static void tcp_xmit_timer __P((struct tcpcb *, int)); static int tcp_newreno __P((struct tcpcb *, struct tcphdr *)); /* Neighbor Discovery, Neighbor Unreachability Detection Upper layer hint. */ #ifdef INET6 #define ND6_HINT(tp) \ do { \ if ((tp) && (tp)->t_inpcb && \ ((tp)->t_inpcb->inp_vflag & INP_IPV6) != 0 && \ (tp)->t_inpcb->in6p_route.ro_rt) \ - nd6_nud_hint((tp)->t_inpcb->in6p_route.ro_rt, NULL); \ + nd6_nud_hint((tp)->t_inpcb->in6p_route.ro_rt, NULL, 0); \ } while (0) #else #define ND6_HINT(tp) #endif /* * Insert segment which inludes th into reassembly queue of tcp with * control block tp. Return TH_FIN if reassembly now includes * a segment with FIN. The macro form does the common case inline * (segment is the next to be received on an established connection, * and the queue is empty), avoiding linkage into and removal * from the queue and repetition of various conversions. * Set DELACK for segments received in order, but ack immediately * when segments are out of order (so fast retransmit can work). */ #define TCP_REASS(tp, th, tlenp, m, so, flags) { \ if ((th)->th_seq == (tp)->rcv_nxt && \ LIST_EMPTY(&(tp)->t_segq) && \ (tp)->t_state == TCPS_ESTABLISHED) { \ if (tcp_delack_enabled) \ callout_reset(tp->tt_delack, tcp_delacktime, \ tcp_timer_delack, tp); \ else \ tp->t_flags |= TF_ACKNOW; \ (tp)->rcv_nxt += *(tlenp); \ flags = (th)->th_flags & TH_FIN; \ tcpstat.tcps_rcvpack++;\ tcpstat.tcps_rcvbyte += *(tlenp);\ ND6_HINT(tp); \ sbappend(&(so)->so_rcv, (m)); \ sorwakeup(so); \ } else { \ (flags) = tcp_reass((tp), (th), (tlenp), (m)); \ tp->t_flags |= TF_ACKNOW; \ } \ } static int tcp_reass(tp, th, tlenp, m) register struct tcpcb *tp; register struct tcphdr *th; int *tlenp; struct mbuf *m; { struct tseg_qent *q; struct tseg_qent *p = NULL; struct tseg_qent *nq; struct tseg_qent *te; struct socket *so = tp->t_inpcb->inp_socket; int flags; /* * Call with th==0 after become established to * force pre-ESTABLISHED data up to user socket. */ if (th == 0) goto present; /* Allocate a new queue entry. If we can't, just drop the pkt. XXX */ MALLOC(te, struct tseg_qent *, sizeof (struct tseg_qent), M_TSEGQ, M_NOWAIT); if (te == NULL) { tcpstat.tcps_rcvmemdrop++; m_freem(m); return (0); } /* * Find a segment which begins after this one does. */ LIST_FOREACH(q, &tp->t_segq, tqe_q) { if (SEQ_GT(q->tqe_th->th_seq, th->th_seq)) break; p = q; } /* * If there is a preceding segment, it may provide some of * our data already. If so, drop the data from the incoming * segment. If it provides all of our data, drop us. */ if (p != NULL) { register int i; /* conversion to int (in i) handles seq wraparound */ i = p->tqe_th->th_seq + p->tqe_len - th->th_seq; if (i > 0) { if (i >= *tlenp) { tcpstat.tcps_rcvduppack++; tcpstat.tcps_rcvdupbyte += *tlenp; m_freem(m); FREE(te, M_TSEGQ); /* * Try to present any queued data * at the left window edge to the user. * This is needed after the 3-WHS * completes. */ goto present; /* ??? */ } m_adj(m, i); *tlenp -= i; th->th_seq += i; } } tcpstat.tcps_rcvoopack++; tcpstat.tcps_rcvoobyte += *tlenp; /* * While we overlap succeeding segments trim them or, * if they are completely covered, dequeue them. */ while (q) { register int i = (th->th_seq + *tlenp) - q->tqe_th->th_seq; if (i <= 0) break; if (i < q->tqe_len) { q->tqe_th->th_seq += i; q->tqe_len -= i; m_adj(q->tqe_m, i); break; } nq = LIST_NEXT(q, tqe_q); LIST_REMOVE(q, tqe_q); m_freem(q->tqe_m); FREE(q, M_TSEGQ); q = nq; } /* Insert the new segment queue entry into place. */ te->tqe_m = m; te->tqe_th = th; te->tqe_len = *tlenp; if (p == NULL) { LIST_INSERT_HEAD(&tp->t_segq, te, tqe_q); } else { LIST_INSERT_AFTER(p, te, tqe_q); } present: /* * Present data to user, advancing rcv_nxt through * completed sequence space. */ if (!TCPS_HAVEESTABLISHED(tp->t_state)) return (0); q = LIST_FIRST(&tp->t_segq); if (!q || q->tqe_th->th_seq != tp->rcv_nxt) return (0); do { tp->rcv_nxt += q->tqe_len; flags = q->tqe_th->th_flags & TH_FIN; nq = LIST_NEXT(q, tqe_q); LIST_REMOVE(q, tqe_q); if (so->so_state & SS_CANTRCVMORE) m_freem(q->tqe_m); else sbappend(&so->so_rcv, q->tqe_m); FREE(q, M_TSEGQ); q = nq; } while (q && q->tqe_th->th_seq == tp->rcv_nxt); ND6_HINT(tp); sorwakeup(so); return (flags); } /* * TCP input routine, follows pages 65-76 of the * protocol specification dated September, 1981 very closely. */ #ifdef INET6 int tcp6_input(mp, offp, proto) struct mbuf **mp; int *offp, proto; { register struct mbuf *m = *mp; IP6_EXTHDR_CHECK(m, *offp, sizeof(struct tcphdr), IPPROTO_DONE); /* * draft-itojun-ipv6-tcp-to-anycast * better place to put this in? */ if (m->m_flags & M_ANYCAST6) { struct ip6_hdr *ip6; ip6 = mtod(m, struct ip6_hdr *); icmp6_error(m, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADDR, (caddr_t)&ip6->ip6_dst - (caddr_t)ip6); return IPPROTO_DONE; } tcp_input(m, *offp, proto); return IPPROTO_DONE; } #endif void tcp_input(m, off0, proto) register struct mbuf *m; int off0, proto; { register struct tcphdr *th; register struct ip *ip = NULL; register struct ipovly *ipov; register struct inpcb *inp; u_char *optp = NULL; int optlen = 0; int len, tlen, off; int drop_hdrlen; register struct tcpcb *tp = 0; register int thflags; struct socket *so = 0; int todrop, acked, ourfinisacked, needoutput = 0; struct in_addr laddr; #ifdef INET6 struct in6_addr laddr6; #endif int dropsocket = 0; int iss = 0; u_long tiwin; struct tcpopt to; /* options in this segment */ struct rmxp_tao *taop; /* pointer to our TAO cache entry */ struct rmxp_tao tao_noncached; /* in case there's no cached entry */ #ifdef TCPDEBUG short ostate = 0; #endif #ifdef INET6 struct ip6_hdr *ip6 = NULL; int isipv6; #endif /* INET6 */ #ifdef INET6 isipv6 = (mtod(m, struct ip *)->ip_v == 6) ? 1 : 0; #endif bzero((char *)&to, sizeof(to)); tcpstat.tcps_rcvtotal++; #ifdef INET6 if (isipv6) { /* IP6_EXTHDR_CHECK() is already done at tcp6_input() */ ip6 = mtod(m, struct ip6_hdr *); tlen = sizeof(*ip6) + ntohs(ip6->ip6_plen) - off0; if (in6_cksum(m, IPPROTO_TCP, off0, tlen)) { tcpstat.tcps_rcvbadsum++; goto drop; } th = (struct tcphdr *)((caddr_t)ip6 + off0); } else #endif /* INET6 */ { /* * Get IP and TCP header together in first mbuf. * Note: IP leaves IP header in first mbuf. */ if (off0 > sizeof (struct ip)) { ip_stripoptions(m, (struct mbuf *)0); off0 = sizeof(struct ip); } if (m->m_len < sizeof (struct tcpiphdr)) { if ((m = m_pullup(m, sizeof (struct tcpiphdr))) == 0) { tcpstat.tcps_rcvshort++; return; } } ip = mtod(m, struct ip *); ipov = (struct ipovly *)ip; th = (struct tcphdr *)((caddr_t)ip + off0); tlen = ip->ip_len; if (m->m_pkthdr.csum_flags & CSUM_DATA_VALID) { if (m->m_pkthdr.csum_flags & CSUM_PSEUDO_HDR) th->th_sum = m->m_pkthdr.csum_data; else th->th_sum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr, htonl(m->m_pkthdr.csum_data + ip->ip_len + IPPROTO_TCP)); th->th_sum ^= 0xffff; } else { /* * Checksum extended TCP header and data. */ len = sizeof (struct ip) + tlen; bzero(ipov->ih_x1, sizeof(ipov->ih_x1)); ipov->ih_len = (u_short)tlen; HTONS(ipov->ih_len); th->th_sum = in_cksum(m, len); } if (th->th_sum) { tcpstat.tcps_rcvbadsum++; goto drop; } #ifdef INET6 /* Re-initialization for later version check */ ip->ip_v = IPVERSION; #endif } /* * Check that TCP offset makes sense, * pull out TCP options and adjust length. XXX */ off = th->th_off << 2; if (off < sizeof (struct tcphdr) || off > tlen) { tcpstat.tcps_rcvbadoff++; goto drop; } tlen -= off; /* tlen is used instead of ti->ti_len */ if (off > sizeof (struct tcphdr)) { #ifdef INET6 if (isipv6) { IP6_EXTHDR_CHECK(m, off0, off, ); ip6 = mtod(m, struct ip6_hdr *); th = (struct tcphdr *)((caddr_t)ip6 + off0); } else #endif /* INET6 */ { if (m->m_len < sizeof(struct ip) + off) { if ((m = m_pullup(m, sizeof (struct ip) + off)) == 0) { tcpstat.tcps_rcvshort++; return; } ip = mtod(m, struct ip *); ipov = (struct ipovly *)ip; th = (struct tcphdr *)((caddr_t)ip + off0); } } optlen = off - sizeof (struct tcphdr); optp = (u_char *)(th + 1); } thflags = th->th_flags; #ifdef TCP_DROP_SYNFIN /* * If the drop_synfin option is enabled, drop all packets with * both the SYN and FIN bits set. This prevents e.g. nmap from * identifying the TCP/IP stack. * * This is incompatible with RFC1644 extensions (T/TCP). */ if (drop_synfin && (thflags & (TH_SYN|TH_FIN)) == (TH_SYN|TH_FIN)) goto drop; #endif /* * Convert TCP protocol specific fields to host format. */ NTOHL(th->th_seq); NTOHL(th->th_ack); NTOHS(th->th_win); NTOHS(th->th_urp); /* * Delay droping TCP, IP headers, IPv6 ext headers, and TCP options, * until after ip6_savecontrol() is called and before other functions * which don't want those proto headers. * Because ip6_savecontrol() is going to parse the mbuf to * search for data to be passed up to user-land, it wants mbuf * parameters to be unchanged. */ drop_hdrlen = off0 + off; /* * Locate pcb for segment. */ findpcb: #ifdef IPFIREWALL_FORWARD if (ip_fw_fwd_addr != NULL #ifdef INET6 && isipv6 == NULL /* IPv6 support is not yet */ #endif /* INET6 */ ) { /* * Diverted. Pretend to be the destination. * already got one like this? */ inp = in_pcblookup_hash(&tcbinfo, ip->ip_src, th->th_sport, ip->ip_dst, th->th_dport, 0, m->m_pkthdr.rcvif); if (!inp) { /* * No, then it's new. Try find the ambushing socket */ if (!ip_fw_fwd_addr->sin_port) { inp = in_pcblookup_hash(&tcbinfo, ip->ip_src, th->th_sport, ip_fw_fwd_addr->sin_addr, th->th_dport, 1, m->m_pkthdr.rcvif); } else { inp = in_pcblookup_hash(&tcbinfo, ip->ip_src, th->th_sport, ip_fw_fwd_addr->sin_addr, ntohs(ip_fw_fwd_addr->sin_port), 1, m->m_pkthdr.rcvif); } } ip_fw_fwd_addr = NULL; } else #endif /* IPFIREWALL_FORWARD */ { #ifdef INET6 if (isipv6) inp = in6_pcblookup_hash(&tcbinfo, &ip6->ip6_src, th->th_sport, &ip6->ip6_dst, th->th_dport, 1, m->m_pkthdr.rcvif); else #endif /* INET6 */ inp = in_pcblookup_hash(&tcbinfo, ip->ip_src, th->th_sport, ip->ip_dst, th->th_dport, 1, m->m_pkthdr.rcvif); } #ifdef IPSEC #ifdef INET6 if (isipv6) { if (inp != NULL && ipsec6_in_reject_so(m, inp->inp_socket)) { ipsec6stat.in_polvio++; goto drop; } } else #endif /* INET6 */ if (inp != NULL && ipsec4_in_reject_so(m, inp->inp_socket)) { ipsecstat.in_polvio++; goto drop; } #endif /*IPSEC*/ /* * If the state is CLOSED (i.e., TCB does not exist) then * all data in the incoming segment is discarded. * If the TCB exists but is in CLOSED state, it is embryonic, * but should either do a listen or a connect soon. */ if (inp == NULL) { if (log_in_vain) { #ifdef INET6 char dbuf[INET6_ADDRSTRLEN], sbuf[INET6_ADDRSTRLEN]; #else /* INET6 */ char dbuf[4*sizeof "123"], sbuf[4*sizeof "123"]; #endif /* INET6 */ #ifdef INET6 if (isipv6) { strcpy(dbuf, ip6_sprintf(&ip6->ip6_dst)); strcpy(sbuf, ip6_sprintf(&ip6->ip6_src)); } else #endif { strcpy(dbuf, inet_ntoa(ip->ip_dst)); strcpy(sbuf, inet_ntoa(ip->ip_src)); } switch (log_in_vain) { case 1: if(thflags & TH_SYN) log(LOG_INFO, "Connection attempt to TCP %s:%d from %s:%d\n", dbuf, ntohs(th->th_dport), sbuf, ntohs(th->th_sport)); break; case 2: log(LOG_INFO, "Connection attempt to TCP %s:%d from %s:%d flags:0x%x\n", dbuf, ntohs(th->th_dport), sbuf, ntohs(th->th_sport), thflags); break; default: break; } } if (blackhole) { switch (blackhole) { case 1: if (thflags & TH_SYN) goto drop; break; case 2: goto drop; default: goto drop; } } goto maybedropwithreset; } tp = intotcpcb(inp); if (tp == 0) goto maybedropwithreset; if (tp->t_state == TCPS_CLOSED) goto drop; /* Unscale the window into a 32-bit value. */ if ((thflags & TH_SYN) == 0) tiwin = th->th_win << tp->snd_scale; else tiwin = th->th_win; #ifdef INET6 /* save packet options if user wanted */ if (isipv6 && inp->in6p_flags & INP_CONTROLOPTS) { if (inp->in6p_options) { m_freem(inp->in6p_options); inp->in6p_options = 0; } ip6_savecontrol(inp, &inp->in6p_options, ip6, m); } /* else, should also do ip_srcroute() here? */ #endif /* INET6 */ so = inp->inp_socket; if (so->so_options & (SO_DEBUG|SO_ACCEPTCONN)) { #ifdef TCPDEBUG if (so->so_options & SO_DEBUG) { ostate = tp->t_state; #ifdef INET6 if (isipv6) bcopy((char *)ip6, (char *)tcp_saveipgen, sizeof(*ip6)); else #endif /* INET6 */ bcopy((char *)ip, (char *)tcp_saveipgen, sizeof(*ip)); tcp_savetcp = *th; } #endif if (so->so_options & SO_ACCEPTCONN) { register struct tcpcb *tp0 = tp; struct socket *so2; #ifdef IPSEC struct socket *oso; #endif #ifdef INET6 struct inpcb *oinp = sotoinpcb(so); #endif /* INET6 */ #ifndef IPSEC /* * Current IPsec implementation makes incorrect IPsec * cache if this check is done here. * So delay this until duplicated socket is created. */ if ((thflags & (TH_RST|TH_ACK|TH_SYN)) != TH_SYN) { /* * Note: dropwithreset makes sure we don't * send a RST in response to a RST. */ if (thflags & TH_ACK) { tcpstat.tcps_badsyn++; goto maybedropwithreset; } goto drop; } #endif so2 = sonewconn(so, 0); if (so2 == 0) { tcpstat.tcps_listendrop++; so2 = sodropablereq(so); if (so2) { tcp_drop(sototcpcb(so2), ETIMEDOUT); so2 = sonewconn(so, 0); } if (!so2) goto drop; } #ifdef IPSEC oso = so; #endif so = so2; /* * This is ugly, but .... * * Mark socket as temporary until we're * committed to keeping it. The code at * ``drop'' and ``dropwithreset'' check the * flag dropsocket to see if the temporary * socket created here should be discarded. * We mark the socket as discardable until * we're committed to it below in TCPS_LISTEN. */ dropsocket++; inp = (struct inpcb *)so->so_pcb; #ifdef INET6 if (isipv6) inp->in6p_laddr = ip6->ip6_dst; else { if ((inp->inp_flags & IN6P_BINDV6ONLY) == 0) { inp->inp_vflag &= ~INP_IPV6; inp->inp_vflag |= INP_IPV4; } #endif /* INET6 */ inp->inp_laddr = ip->ip_dst; #ifdef INET6 } #endif /* INET6 */ inp->inp_lport = th->th_dport; if (in_pcbinshash(inp) != 0) { /* * Undo the assignments above if we failed to * put the PCB on the hash lists. */ #ifdef INET6 if (isipv6) inp->in6p_laddr = in6addr_any; else #endif /* INET6 */ inp->inp_laddr.s_addr = INADDR_ANY; inp->inp_lport = 0; goto drop; } #ifdef IPSEC /* * To avoid creating incorrectly cached IPsec * association, this is need to be done here. * * Subject: (KAME-snap 748) * From: Wayne Knowles * ftp://ftp.kame.net/pub/mail-list/snap-users/748 */ if ((thflags & (TH_RST|TH_ACK|TH_SYN)) != TH_SYN) { /* * Note: dropwithreset makes sure we don't * send a RST in response to a RST. */ if (thflags & TH_ACK) { tcpstat.tcps_badsyn++; goto maybedropwithreset; } goto drop; } #endif #ifdef INET6 if (isipv6) { /* * inherit socket options from the listening * socket. */ inp->inp_flags |= oinp->inp_flags & INP_CONTROLOPTS; if (inp->inp_flags & INP_CONTROLOPTS) { if (inp->in6p_options) { m_freem(inp->in6p_options); inp->in6p_options = 0; } ip6_savecontrol(inp, &inp->in6p_options, ip6, m); } } else #endif /* INET6 */ inp->inp_options = ip_srcroute(); #ifdef IPSEC /* copy old policy into new socket's */ if (ipsec_copy_policy(sotoinpcb(oso)->inp_sp, inp->inp_sp)) printf("tcp_input: could not copy policy\n"); #endif tp = intotcpcb(inp); tp->t_state = TCPS_LISTEN; tp->t_flags |= tp0->t_flags & (TF_NOPUSH|TF_NOOPT); /* Compute proper scaling value from buffer space */ while (tp->request_r_scale < TCP_MAX_WINSHIFT && TCP_MAXWIN << tp->request_r_scale < so->so_rcv.sb_hiwat) tp->request_r_scale++; } } /* * Segment received on connection. * Reset idle time and keep-alive timer. */ tp->t_rcvtime = ticks; if (TCPS_HAVEESTABLISHED(tp->t_state)) callout_reset(tp->tt_keep, tcp_keepidle, tcp_timer_keep, tp); /* * Process options if not in LISTEN state, * else do it below (after getting remote address). */ if (tp->t_state != TCPS_LISTEN) tcp_dooptions(tp, optp, optlen, th, &to); /* * Header prediction: check for the two common cases * of a uni-directional data xfer. If the packet has * no control flags, is in-sequence, the window didn't * change and we're not retransmitting, it's a * candidate. If the length is zero and the ack moved * forward, we're the sender side of the xfer. Just * free the data acked & wake any higher level process * that was blocked waiting for space. If the length * is non-zero and the ack didn't move, we're the * receiver side. If we're getting packets in-order * (the reassembly queue is empty), add the data to * the socket buffer and note that we need a delayed ack. * Make sure that the hidden state-flags are also off. * Since we check for TCPS_ESTABLISHED above, it can only * be TH_NEEDSYN. */ if (tp->t_state == TCPS_ESTABLISHED && (thflags & (TH_SYN|TH_FIN|TH_RST|TH_URG|TH_ACK)) == TH_ACK && ((tp->t_flags & (TF_NEEDSYN|TF_NEEDFIN)) == 0) && ((to.to_flag & TOF_TS) == 0 || TSTMP_GEQ(to.to_tsval, tp->ts_recent)) && /* * Using the CC option is compulsory if once started: * the segment is OK if no T/TCP was negotiated or * if the segment has a CC option equal to CCrecv */ ((tp->t_flags & (TF_REQ_CC|TF_RCVD_CC)) != (TF_REQ_CC|TF_RCVD_CC) || ((to.to_flag & TOF_CC) != 0 && to.to_cc == tp->cc_recv)) && th->th_seq == tp->rcv_nxt && tiwin && tiwin == tp->snd_wnd && tp->snd_nxt == tp->snd_max) { /* * If last ACK falls within this segment's sequence numbers, * record the timestamp. * NOTE that the test is modified according to the latest * proposal of the tcplw@cray.com list (Braden 1993/04/26). */ if ((to.to_flag & TOF_TS) != 0 && SEQ_LEQ(th->th_seq, tp->last_ack_sent)) { tp->ts_recent_age = ticks; tp->ts_recent = to.to_tsval; } if (tlen == 0) { if (SEQ_GT(th->th_ack, tp->snd_una) && SEQ_LEQ(th->th_ack, tp->snd_max) && tp->snd_cwnd >= tp->snd_wnd && tp->t_dupacks < tcprexmtthresh) { /* * this is a pure ack for outstanding data. */ ++tcpstat.tcps_predack; /* * "bad retransmit" recovery */ if (tp->t_rxtshift == 1 && ticks < tp->t_badrxtwin) { tp->snd_cwnd = tp->snd_cwnd_prev; tp->snd_ssthresh = tp->snd_ssthresh_prev; tp->snd_nxt = tp->snd_max; tp->t_badrxtwin = 0; } if ((to.to_flag & TOF_TS) != 0) tcp_xmit_timer(tp, ticks - to.to_tsecr + 1); else if (tp->t_rtttime && SEQ_GT(th->th_ack, tp->t_rtseq)) tcp_xmit_timer(tp, ticks - tp->t_rtttime); acked = th->th_ack - tp->snd_una; tcpstat.tcps_rcvackpack++; tcpstat.tcps_rcvackbyte += acked; sbdrop(&so->so_snd, acked); tp->snd_una = th->th_ack; m_freem(m); ND6_HINT(tp); /* some progress has been done */ /* * If all outstanding data are acked, stop * retransmit timer, otherwise restart timer * using current (possibly backed-off) value. * If process is waiting for space, * wakeup/selwakeup/signal. If data * are ready to send, let tcp_output * decide between more output or persist. */ if (tp->snd_una == tp->snd_max) callout_stop(tp->tt_rexmt); else if (!callout_active(tp->tt_persist)) callout_reset(tp->tt_rexmt, tp->t_rxtcur, tcp_timer_rexmt, tp); sowwakeup(so); if (so->so_snd.sb_cc) (void) tcp_output(tp); return; } } else if (th->th_ack == tp->snd_una && LIST_EMPTY(&tp->t_segq) && tlen <= sbspace(&so->so_rcv)) { /* * this is a pure, in-sequence data packet * with nothing on the reassembly queue and * we have enough buffer space to take it. */ ++tcpstat.tcps_preddat; tp->rcv_nxt += tlen; tcpstat.tcps_rcvpack++; tcpstat.tcps_rcvbyte += tlen; ND6_HINT(tp); /* some progress has been done */ /* * Add data to socket buffer. */ m_adj(m, drop_hdrlen); /* delayed header drop */ sbappend(&so->so_rcv, m); sorwakeup(so); if (tcp_delack_enabled) { callout_reset(tp->tt_delack, tcp_delacktime, tcp_timer_delack, tp); } else { tp->t_flags |= TF_ACKNOW; tcp_output(tp); } return; } } /* * Calculate amount of space in receive window, * and then do TCP input processing. * Receive window is amount of space in rcv queue, * but not less than advertised window. */ { int win; win = sbspace(&so->so_rcv); if (win < 0) win = 0; tp->rcv_wnd = imax(win, (int)(tp->rcv_adv - tp->rcv_nxt)); } switch (tp->t_state) { /* * If the state is LISTEN then ignore segment if it contains an RST. * If the segment contains an ACK then it is bad and send a RST. * If it does not contain a SYN then it is not interesting; drop it. * If it is from this socket, drop it, it must be forged. * Don't bother responding if the destination was a broadcast. * Otherwise initialize tp->rcv_nxt, and tp->irs, select an initial * tp->iss, and send a segment: * * Also initialize tp->snd_nxt to tp->iss+1 and tp->snd_una to tp->iss. * Fill in remote peer address fields if not previously specified. * Enter SYN_RECEIVED state, and process any other fields of this * segment in this state. */ case TCPS_LISTEN: { register struct sockaddr_in *sin; #ifdef INET6 register struct sockaddr_in6 *sin6; #endif if (thflags & TH_RST) goto drop; if (thflags & TH_ACK) goto maybedropwithreset; if ((thflags & TH_SYN) == 0) goto drop; if (th->th_dport == th->th_sport) { #ifdef INET6 if (isipv6) { if (IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &ip6->ip6_src)) goto drop; } else #endif /* INET6 */ if (ip->ip_dst.s_addr == ip->ip_src.s_addr) goto drop; } /* * RFC1122 4.2.3.10, p. 104: discard bcast/mcast SYN * in_broadcast() should never return true on a received * packet with M_BCAST not set. * * Packets with a multicast source address should also * be discarded. */ if (m->m_flags & (M_BCAST|M_MCAST)) goto drop; #ifdef INET6 if (isipv6) { if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) || IN6_IS_ADDR_MULTICAST(&ip6->ip6_src)) goto drop; } else #endif if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr)) || IN_MULTICAST(ntohl(ip->ip_src.s_addr)) || ip->ip_src.s_addr == htonl(INADDR_BROADCAST)) goto drop; #ifdef INET6 if (isipv6) { MALLOC(sin6, struct sockaddr_in6 *, sizeof *sin6, M_SONAME, M_NOWAIT); if (sin6 == NULL) goto drop; bzero(sin6, sizeof(*sin6)); sin6->sin6_family = AF_INET6; sin6->sin6_len = sizeof(*sin6); sin6->sin6_addr = ip6->ip6_src; sin6->sin6_port = th->th_sport; laddr6 = inp->in6p_laddr; if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr)) inp->in6p_laddr = ip6->ip6_dst; if (in6_pcbconnect(inp, (struct sockaddr *)sin6, &proc0)) { inp->in6p_laddr = laddr6; FREE(sin6, M_SONAME); goto drop; } FREE(sin6, M_SONAME); } else #endif { MALLOC(sin, struct sockaddr_in *, sizeof *sin, M_SONAME, M_NOWAIT); if (sin == NULL) goto drop; sin->sin_family = AF_INET; sin->sin_len = sizeof(*sin); sin->sin_addr = ip->ip_src; sin->sin_port = th->th_sport; bzero((caddr_t)sin->sin_zero, sizeof(sin->sin_zero)); laddr = inp->inp_laddr; if (inp->inp_laddr.s_addr == INADDR_ANY) inp->inp_laddr = ip->ip_dst; if (in_pcbconnect(inp, (struct sockaddr *)sin, &proc0)) { inp->inp_laddr = laddr; FREE(sin, M_SONAME); goto drop; } FREE(sin, M_SONAME); } tp->t_template = tcp_template(tp); if (tp->t_template == 0) { tp = tcp_drop(tp, ENOBUFS); dropsocket = 0; /* socket is already gone */ goto drop; } if ((taop = tcp_gettaocache(inp)) == NULL) { taop = &tao_noncached; bzero(taop, sizeof(*taop)); } tcp_dooptions(tp, optp, optlen, th, &to); if (iss) tp->iss = iss; else tp->iss = tcp_iss; tcp_iss += TCP_ISSINCR/4; tp->irs = th->th_seq; tcp_sendseqinit(tp); tcp_rcvseqinit(tp); tp->snd_recover = tp->snd_una; /* * Initialization of the tcpcb for transaction; * set SND.WND = SEG.WND, * initialize CCsend and CCrecv. */ tp->snd_wnd = tiwin; /* initial send-window */ tp->cc_send = CC_INC(tcp_ccgen); tp->cc_recv = to.to_cc; /* * Perform TAO test on incoming CC (SEG.CC) option, if any. * - compare SEG.CC against cached CC from the same host, * if any. * - if SEG.CC > chached value, SYN must be new and is accepted * immediately: save new CC in the cache, mark the socket * connected, enter ESTABLISHED state, turn on flag to * send a SYN in the next segment. * A virtual advertised window is set in rcv_adv to * initialize SWS prevention. Then enter normal segment * processing: drop SYN, process data and FIN. * - otherwise do a normal 3-way handshake. */ if ((to.to_flag & TOF_CC) != 0) { if (((tp->t_flags & TF_NOPUSH) != 0) && taop->tao_cc != 0 && CC_GT(to.to_cc, taop->tao_cc)) { taop->tao_cc = to.to_cc; tp->t_starttime = ticks; tp->t_state = TCPS_ESTABLISHED; /* * If there is a FIN, or if there is data and the * connection is local, then delay SYN,ACK(SYN) in * the hope of piggy-backing it on a response * segment. Otherwise must send ACK now in case * the other side is slow starting. */ if (tcp_delack_enabled && ((thflags & TH_FIN) || (tlen != 0 && #ifdef INET6 ((isipv6 && in6_localaddr(&inp->in6p_faddr)) || (!isipv6 && #endif in_localaddr(inp->inp_faddr) #ifdef INET6 )) #endif ))) { callout_reset(tp->tt_delack, tcp_delacktime, tcp_timer_delack, tp); tp->t_flags |= TF_NEEDSYN; } else tp->t_flags |= (TF_ACKNOW | TF_NEEDSYN); /* * Limit the `virtual advertised window' to TCP_MAXWIN * here. Even if we requested window scaling, it will * become effective only later when our SYN is acked. */ tp->rcv_adv += min(tp->rcv_wnd, TCP_MAXWIN); tcpstat.tcps_connects++; soisconnected(so); callout_reset(tp->tt_keep, tcp_keepinit, tcp_timer_keep, tp); dropsocket = 0; /* committed to socket */ tcpstat.tcps_accepts++; goto trimthenstep6; } /* else do standard 3-way handshake */ } else { /* * No CC option, but maybe CC.NEW: * invalidate cached value. */ taop->tao_cc = 0; } /* * TAO test failed or there was no CC option, * do a standard 3-way handshake. */ tp->t_flags |= TF_ACKNOW; tp->t_state = TCPS_SYN_RECEIVED; callout_reset(tp->tt_keep, tcp_keepinit, tcp_timer_keep, tp); dropsocket = 0; /* committed to socket */ tcpstat.tcps_accepts++; goto trimthenstep6; } /* * If the state is SYN_RECEIVED: * if seg contains an ACK, but not for our SYN/ACK, send a RST. */ case TCPS_SYN_RECEIVED: if ((thflags & TH_ACK) && (SEQ_LEQ(th->th_ack, tp->snd_una) || SEQ_GT(th->th_ack, tp->snd_max))) goto maybedropwithreset; break; /* * If the state is SYN_SENT: * if seg contains an ACK, but not for our SYN, drop the input. * if seg contains a RST, then drop the connection. * if seg does not contain SYN, then drop it. * Otherwise this is an acceptable SYN segment * initialize tp->rcv_nxt and tp->irs * if seg contains ack then advance tp->snd_una * if SYN has been acked change to ESTABLISHED else SYN_RCVD state * arrange for segment to be acked (eventually) * continue processing rest of data/controls, beginning with URG */ case TCPS_SYN_SENT: if ((taop = tcp_gettaocache(inp)) == NULL) { taop = &tao_noncached; bzero(taop, sizeof(*taop)); } if ((thflags & TH_ACK) && (SEQ_LEQ(th->th_ack, tp->iss) || SEQ_GT(th->th_ack, tp->snd_max))) { /* * If we have a cached CCsent for the remote host, * hence we haven't just crashed and restarted, * do not send a RST. This may be a retransmission * from the other side after our earlier ACK was lost. * Our new SYN, when it arrives, will serve as the * needed ACK. */ if (taop->tao_ccsent != 0) goto drop; else goto dropwithreset; } if (thflags & TH_RST) { if (thflags & TH_ACK) tp = tcp_drop(tp, ECONNREFUSED); goto drop; } if ((thflags & TH_SYN) == 0) goto drop; tp->snd_wnd = th->th_win; /* initial send window */ tp->cc_recv = to.to_cc; /* foreign CC */ tp->irs = th->th_seq; tcp_rcvseqinit(tp); if (thflags & TH_ACK) { /* * Our SYN was acked. If segment contains CC.ECHO * option, check it to make sure this segment really * matches our SYN. If not, just drop it as old * duplicate, but send an RST if we're still playing * by the old rules. If no CC.ECHO option, make sure * we don't get fooled into using T/TCP. */ if (to.to_flag & TOF_CCECHO) { if (tp->cc_send != to.to_ccecho) { if (taop->tao_ccsent != 0) goto drop; else goto dropwithreset; } } else tp->t_flags &= ~TF_RCVD_CC; tcpstat.tcps_connects++; soisconnected(so); /* Do window scaling on this connection? */ if ((tp->t_flags & (TF_RCVD_SCALE|TF_REQ_SCALE)) == (TF_RCVD_SCALE|TF_REQ_SCALE)) { tp->snd_scale = tp->requested_s_scale; tp->rcv_scale = tp->request_r_scale; } /* Segment is acceptable, update cache if undefined. */ if (taop->tao_ccsent == 0) taop->tao_ccsent = to.to_ccecho; tp->rcv_adv += tp->rcv_wnd; tp->snd_una++; /* SYN is acked */ /* * If there's data, delay ACK; if there's also a FIN * ACKNOW will be turned on later. */ if (tcp_delack_enabled && tlen != 0) callout_reset(tp->tt_delack, tcp_delacktime, tcp_timer_delack, tp); else tp->t_flags |= TF_ACKNOW; /* * Received in SYN_SENT[*] state. * Transitions: * SYN_SENT --> ESTABLISHED * SYN_SENT* --> FIN_WAIT_1 */ tp->t_starttime = ticks; if (tp->t_flags & TF_NEEDFIN) { tp->t_state = TCPS_FIN_WAIT_1; tp->t_flags &= ~TF_NEEDFIN; thflags &= ~TH_SYN; } else { tp->t_state = TCPS_ESTABLISHED; callout_reset(tp->tt_keep, tcp_keepidle, tcp_timer_keep, tp); } } else { /* * Received initial SYN in SYN-SENT[*] state => simul- * taneous open. If segment contains CC option and there is * a cached CC, apply TAO test; if it succeeds, connection is * half-synchronized. Otherwise, do 3-way handshake: * SYN-SENT -> SYN-RECEIVED * SYN-SENT* -> SYN-RECEIVED* * If there was no CC option, clear cached CC value. */ tp->t_flags |= TF_ACKNOW; callout_stop(tp->tt_rexmt); if (to.to_flag & TOF_CC) { if (taop->tao_cc != 0 && CC_GT(to.to_cc, taop->tao_cc)) { /* * update cache and make transition: * SYN-SENT -> ESTABLISHED* * SYN-SENT* -> FIN-WAIT-1* */ taop->tao_cc = to.to_cc; tp->t_starttime = ticks; if (tp->t_flags & TF_NEEDFIN) { tp->t_state = TCPS_FIN_WAIT_1; tp->t_flags &= ~TF_NEEDFIN; } else { tp->t_state = TCPS_ESTABLISHED; callout_reset(tp->tt_keep, tcp_keepidle, tcp_timer_keep, tp); } tp->t_flags |= TF_NEEDSYN; } else tp->t_state = TCPS_SYN_RECEIVED; } else { /* CC.NEW or no option => invalidate cache */ taop->tao_cc = 0; tp->t_state = TCPS_SYN_RECEIVED; } } trimthenstep6: /* * Advance th->th_seq to correspond to first data byte. * If data, trim to stay within window, * dropping FIN if necessary. */ th->th_seq++; if (tlen > tp->rcv_wnd) { todrop = tlen - tp->rcv_wnd; m_adj(m, -todrop); tlen = tp->rcv_wnd; thflags &= ~TH_FIN; tcpstat.tcps_rcvpackafterwin++; tcpstat.tcps_rcvbyteafterwin += todrop; } tp->snd_wl1 = th->th_seq - 1; tp->rcv_up = th->th_seq; /* * Client side of transaction: already sent SYN and data. * If the remote host used T/TCP to validate the SYN, * our data will be ACK'd; if so, enter normal data segment * processing in the middle of step 5, ack processing. * Otherwise, goto step 6. */ if (thflags & TH_ACK) goto process_ACK; goto step6; /* * If the state is LAST_ACK or CLOSING or TIME_WAIT: * if segment contains a SYN and CC [not CC.NEW] option: * if state == TIME_WAIT and connection duration > MSL, * drop packet and send RST; * * if SEG.CC > CCrecv then is new SYN, and can implicitly * ack the FIN (and data) in retransmission queue. * Complete close and delete TCPCB. Then reprocess * segment, hoping to find new TCPCB in LISTEN state; * * else must be old SYN; drop it. * else do normal processing. */ case TCPS_LAST_ACK: case TCPS_CLOSING: case TCPS_TIME_WAIT: if ((thflags & TH_SYN) && (to.to_flag & TOF_CC) && tp->cc_recv != 0) { if (tp->t_state == TCPS_TIME_WAIT && (ticks - tp->t_starttime) > tcp_msl) goto dropwithreset; if (CC_GT(to.to_cc, tp->cc_recv)) { tp = tcp_close(tp); goto findpcb; } else goto drop; } break; /* continue normal processing */ } /* * States other than LISTEN or SYN_SENT. * First check the RST flag and sequence number since reset segments * are exempt from the timestamp and connection count tests. This * fixes a bug introduced by the Stevens, vol. 2, p. 960 bugfix * below which allowed reset segments in half the sequence space * to fall though and be processed (which gives forged reset * segments with a random sequence number a 50 percent chance of * killing a connection). * Then check timestamp, if present. * Then check the connection count, if present. * Then check that at least some bytes of segment are within * receive window. If segment begins before rcv_nxt, * drop leading data (and SYN); if nothing left, just ack. * * * If the RST bit is set, check the sequence number to see * if this is a valid reset segment. * RFC 793 page 37: * In all states except SYN-SENT, all reset (RST) segments * are validated by checking their SEQ-fields. A reset is * valid if its sequence number is in the window. * Note: this does not take into account delayed ACKs, so * we should test against last_ack_sent instead of rcv_nxt. * The sequence number in the reset segment is normally an * echo of our outgoing acknowlegement numbers, but some hosts * send a reset with the sequence number at the rightmost edge * of our receive window, and we have to handle this case. * If we have multiple segments in flight, the intial reset * segment sequence numbers will be to the left of last_ack_sent, * but they will eventually catch up. * In any case, it never made sense to trim reset segments to * fit the receive window since RFC 1122 says: * 4.2.2.12 RST Segment: RFC-793 Section 3.4 * * A TCP SHOULD allow a received RST segment to include data. * * DISCUSSION * It has been suggested that a RST segment could contain * ASCII text that encoded and explained the cause of the * RST. No standard has yet been established for such * data. * * If the reset segment passes the sequence number test examine * the state: * SYN_RECEIVED STATE: * If passive open, return to LISTEN state. * If active open, inform user that connection was refused. * ESTABLISHED, FIN_WAIT_1, FIN_WAIT2, CLOSE_WAIT STATES: * Inform user that connection was reset, and close tcb. * CLOSING, LAST_ACK STATES: * Close the tcb. * TIME_WAIT STATE: * Drop the segment - see Stevens, vol. 2, p. 964 and * RFC 1337. */ if (thflags & TH_RST) { if (SEQ_GEQ(th->th_seq, tp->last_ack_sent) && SEQ_LT(th->th_seq, tp->last_ack_sent + tp->rcv_wnd)) { switch (tp->t_state) { case TCPS_SYN_RECEIVED: so->so_error = ECONNREFUSED; goto close; case TCPS_ESTABLISHED: case TCPS_FIN_WAIT_1: case TCPS_FIN_WAIT_2: case TCPS_CLOSE_WAIT: so->so_error = ECONNRESET; close: tp->t_state = TCPS_CLOSED; tcpstat.tcps_drops++; tp = tcp_close(tp); break; case TCPS_CLOSING: case TCPS_LAST_ACK: tp = tcp_close(tp); break; case TCPS_TIME_WAIT: break; } } goto drop; } /* * RFC 1323 PAWS: If we have a timestamp reply on this segment * and it's less than ts_recent, drop it. */ if ((to.to_flag & TOF_TS) != 0 && tp->ts_recent && TSTMP_LT(to.to_tsval, tp->ts_recent)) { /* Check to see if ts_recent is over 24 days old. */ if ((int)(ticks - tp->ts_recent_age) > TCP_PAWS_IDLE) { /* * Invalidate ts_recent. If this segment updates * ts_recent, the age will be reset later and ts_recent * will get a valid value. If it does not, setting * ts_recent to zero will at least satisfy the * requirement that zero be placed in the timestamp * echo reply when ts_recent isn't valid. The * age isn't reset until we get a valid ts_recent * because we don't want out-of-order segments to be * dropped when ts_recent is old. */ tp->ts_recent = 0; } else { tcpstat.tcps_rcvduppack++; tcpstat.tcps_rcvdupbyte += tlen; tcpstat.tcps_pawsdrop++; goto dropafterack; } } /* * T/TCP mechanism * If T/TCP was negotiated and the segment doesn't have CC, * or if its CC is wrong then drop the segment. * RST segments do not have to comply with this. */ if ((tp->t_flags & (TF_REQ_CC|TF_RCVD_CC)) == (TF_REQ_CC|TF_RCVD_CC) && ((to.to_flag & TOF_CC) == 0 || tp->cc_recv != to.to_cc)) goto dropafterack; /* * In the SYN-RECEIVED state, validate that the packet belongs to * this connection before trimming the data to fit the receive * window. Check the sequence number versus IRS since we know * the sequence numbers haven't wrapped. This is a partial fix * for the "LAND" DoS attack. */ if (tp->t_state == TCPS_SYN_RECEIVED && SEQ_LT(th->th_seq, tp->irs)) goto maybedropwithreset; todrop = tp->rcv_nxt - th->th_seq; if (todrop > 0) { if (thflags & TH_SYN) { thflags &= ~TH_SYN; th->th_seq++; if (th->th_urp > 1) th->th_urp--; else thflags &= ~TH_URG; todrop--; } /* * Following if statement from Stevens, vol. 2, p. 960. */ if (todrop > tlen || (todrop == tlen && (thflags & TH_FIN) == 0)) { /* * Any valid FIN must be to the left of the window. * At this point the FIN must be a duplicate or out * of sequence; drop it. */ thflags &= ~TH_FIN; /* * Send an ACK to resynchronize and drop any data. * But keep on processing for RST or ACK. */ tp->t_flags |= TF_ACKNOW; todrop = tlen; tcpstat.tcps_rcvduppack++; tcpstat.tcps_rcvdupbyte += todrop; } else { tcpstat.tcps_rcvpartduppack++; tcpstat.tcps_rcvpartdupbyte += todrop; } drop_hdrlen += todrop; /* drop from the top afterwards */ th->th_seq += todrop; tlen -= todrop; if (th->th_urp > todrop) th->th_urp -= todrop; else { thflags &= ~TH_URG; th->th_urp = 0; } } /* * If new data are received on a connection after the * user processes are gone, then RST the other end. */ if ((so->so_state & SS_NOFDREF) && tp->t_state > TCPS_CLOSE_WAIT && tlen) { tp = tcp_close(tp); tcpstat.tcps_rcvafterclose++; goto dropwithreset; } /* * If segment ends after window, drop trailing data * (and PUSH and FIN); if nothing left, just ACK. */ todrop = (th->th_seq+tlen) - (tp->rcv_nxt+tp->rcv_wnd); if (todrop > 0) { tcpstat.tcps_rcvpackafterwin++; if (todrop >= tlen) { tcpstat.tcps_rcvbyteafterwin += tlen; /* * If a new connection request is received * while in TIME_WAIT, drop the old connection * and start over if the sequence numbers * are above the previous ones. */ if (thflags & TH_SYN && tp->t_state == TCPS_TIME_WAIT && SEQ_GT(th->th_seq, tp->rcv_nxt)) { iss = tp->snd_nxt + TCP_ISSINCR; tp = tcp_close(tp); goto findpcb; } /* * If window is closed can only take segments at * window edge, and have to drop data and PUSH from * incoming segments. Continue processing, but * remember to ack. Otherwise, drop segment * and ack. */ if (tp->rcv_wnd == 0 && th->th_seq == tp->rcv_nxt) { tp->t_flags |= TF_ACKNOW; tcpstat.tcps_rcvwinprobe++; } else goto dropafterack; } else tcpstat.tcps_rcvbyteafterwin += todrop; m_adj(m, -todrop); tlen -= todrop; thflags &= ~(TH_PUSH|TH_FIN); } /* * If last ACK falls within this segment's sequence numbers, * record its timestamp. * NOTE that the test is modified according to the latest * proposal of the tcplw@cray.com list (Braden 1993/04/26). */ if ((to.to_flag & TOF_TS) != 0 && SEQ_LEQ(th->th_seq, tp->last_ack_sent)) { tp->ts_recent_age = ticks; tp->ts_recent = to.to_tsval; } /* * If a SYN is in the window, then this is an * error and we send an RST and drop the connection. */ if (thflags & TH_SYN) { tp = tcp_drop(tp, ECONNRESET); goto dropwithreset; } /* * If the ACK bit is off: if in SYN-RECEIVED state or SENDSYN * flag is on (half-synchronized state), then queue data for * later processing; else drop segment and return. */ if ((thflags & TH_ACK) == 0) { if (tp->t_state == TCPS_SYN_RECEIVED || (tp->t_flags & TF_NEEDSYN)) goto step6; else goto drop; } /* * Ack processing. */ switch (tp->t_state) { /* * In SYN_RECEIVED state, the ack ACKs our SYN, so enter * ESTABLISHED state and continue processing. * The ACK was checked above. */ case TCPS_SYN_RECEIVED: tcpstat.tcps_connects++; soisconnected(so); /* Do window scaling? */ if ((tp->t_flags & (TF_RCVD_SCALE|TF_REQ_SCALE)) == (TF_RCVD_SCALE|TF_REQ_SCALE)) { tp->snd_scale = tp->requested_s_scale; tp->rcv_scale = tp->request_r_scale; } /* * Upon successful completion of 3-way handshake, * update cache.CC if it was undefined, pass any queued * data to the user, and advance state appropriately. */ if ((taop = tcp_gettaocache(inp)) != NULL && taop->tao_cc == 0) taop->tao_cc = tp->cc_recv; /* * Make transitions: * SYN-RECEIVED -> ESTABLISHED * SYN-RECEIVED* -> FIN-WAIT-1 */ tp->t_starttime = ticks; if (tp->t_flags & TF_NEEDFIN) { tp->t_state = TCPS_FIN_WAIT_1; tp->t_flags &= ~TF_NEEDFIN; } else { tp->t_state = TCPS_ESTABLISHED; callout_reset(tp->tt_keep, tcp_keepidle, tcp_timer_keep, tp); } /* * If segment contains data or ACK, will call tcp_reass() * later; if not, do so now to pass queued data to user. */ if (tlen == 0 && (thflags & TH_FIN) == 0) (void) tcp_reass(tp, (struct tcphdr *)0, 0, (struct mbuf *)0); tp->snd_wl1 = th->th_seq - 1; /* fall into ... */ /* * In ESTABLISHED state: drop duplicate ACKs; ACK out of range * ACKs. If the ack is in the range * tp->snd_una < th->th_ack <= tp->snd_max * then advance tp->snd_una to th->th_ack and drop * data from the retransmission queue. If this ACK reflects * more up to date window information we update our window information. */ case TCPS_ESTABLISHED: case TCPS_FIN_WAIT_1: case TCPS_FIN_WAIT_2: case TCPS_CLOSE_WAIT: case TCPS_CLOSING: case TCPS_LAST_ACK: case TCPS_TIME_WAIT: if (SEQ_LEQ(th->th_ack, tp->snd_una)) { if (tlen == 0 && tiwin == tp->snd_wnd) { tcpstat.tcps_rcvdupack++; /* * If we have outstanding data (other than * a window probe), this is a completely * duplicate ack (ie, window info didn't * change), the ack is the biggest we've * seen and we've seen exactly our rexmt * threshhold of them, assume a packet * has been dropped and retransmit it. * Kludge snd_nxt & the congestion * window so we send only this one * packet. * * We know we're losing at the current * window size so do congestion avoidance * (set ssthresh to half the current window * and pull our congestion window back to * the new ssthresh). * * Dup acks mean that packets have left the * network (they're now cached at the receiver) * so bump cwnd by the amount in the receiver * to keep a constant cwnd packets in the * network. */ if (!callout_active(tp->tt_rexmt) || th->th_ack != tp->snd_una) tp->t_dupacks = 0; else if (++tp->t_dupacks == tcprexmtthresh) { tcp_seq onxt = tp->snd_nxt; u_int win = min(tp->snd_wnd, tp->snd_cwnd) / 2 / tp->t_maxseg; if (tcp_do_newreno && SEQ_LT(th->th_ack, tp->snd_recover)) { /* False retransmit, should not * cut window */ tp->snd_cwnd += tp->t_maxseg; tp->t_dupacks = 0; (void) tcp_output(tp); goto drop; } if (win < 2) win = 2; tp->snd_ssthresh = win * tp->t_maxseg; tp->snd_recover = tp->snd_max; callout_stop(tp->tt_rexmt); tp->t_rtttime = 0; tp->snd_nxt = th->th_ack; tp->snd_cwnd = tp->t_maxseg; (void) tcp_output(tp); tp->snd_cwnd = tp->snd_ssthresh + tp->t_maxseg * tp->t_dupacks; if (SEQ_GT(onxt, tp->snd_nxt)) tp->snd_nxt = onxt; goto drop; } else if (tp->t_dupacks > tcprexmtthresh) { tp->snd_cwnd += tp->t_maxseg; (void) tcp_output(tp); goto drop; } } else tp->t_dupacks = 0; break; } /* * If the congestion window was inflated to account * for the other side's cached packets, retract it. */ if (tcp_do_newreno == 0) { if (tp->t_dupacks >= tcprexmtthresh && tp->snd_cwnd > tp->snd_ssthresh) tp->snd_cwnd = tp->snd_ssthresh; tp->t_dupacks = 0; } else if (tp->t_dupacks >= tcprexmtthresh && !tcp_newreno(tp, th)) { /* * Window inflation should have left us with approx. * snd_ssthresh outstanding data. But in case we * would be inclined to send a burst, better to do * it via the slow start mechanism. */ if (SEQ_GT(th->th_ack + tp->snd_ssthresh, tp->snd_max)) tp->snd_cwnd = tp->snd_max - th->th_ack + tp->t_maxseg; else tp->snd_cwnd = tp->snd_ssthresh; tp->t_dupacks = 0; } if (SEQ_GT(th->th_ack, tp->snd_max)) { tcpstat.tcps_rcvacktoomuch++; goto dropafterack; } /* * If we reach this point, ACK is not a duplicate, * i.e., it ACKs something we sent. */ if (tp->t_flags & TF_NEEDSYN) { /* * T/TCP: Connection was half-synchronized, and our * SYN has been ACK'd (so connection is now fully * synchronized). Go to non-starred state, * increment snd_una for ACK of SYN, and check if * we can do window scaling. */ tp->t_flags &= ~TF_NEEDSYN; tp->snd_una++; /* Do window scaling? */ if ((tp->t_flags & (TF_RCVD_SCALE|TF_REQ_SCALE)) == (TF_RCVD_SCALE|TF_REQ_SCALE)) { tp->snd_scale = tp->requested_s_scale; tp->rcv_scale = tp->request_r_scale; } } process_ACK: acked = th->th_ack - tp->snd_una; tcpstat.tcps_rcvackpack++; tcpstat.tcps_rcvackbyte += acked; /* * If we just performed our first retransmit, and the ACK * arrives within our recovery window, then it was a mistake * to do the retransmit in the first place. Recover our * original cwnd and ssthresh, and proceed to transmit where * we left off. */ if (tp->t_rxtshift == 1 && ticks < tp->t_badrxtwin) { tp->snd_cwnd = tp->snd_cwnd_prev; tp->snd_ssthresh = tp->snd_ssthresh_prev; tp->snd_nxt = tp->snd_max; tp->t_badrxtwin = 0; /* XXX probably not required */ } /* * If we have a timestamp reply, update smoothed * round trip time. If no timestamp is present but * transmit timer is running and timed sequence * number was acked, update smoothed round trip time. * Since we now have an rtt measurement, cancel the * timer backoff (cf., Phil Karn's retransmit alg.). * Recompute the initial retransmit timer. */ if (to.to_flag & TOF_TS) tcp_xmit_timer(tp, ticks - to.to_tsecr + 1); else if (tp->t_rtttime && SEQ_GT(th->th_ack, tp->t_rtseq)) tcp_xmit_timer(tp, ticks - tp->t_rtttime); /* * If all outstanding data is acked, stop retransmit * timer and remember to restart (more output or persist). * If there is more data to be acked, restart retransmit * timer, using current (possibly backed-off) value. */ if (th->th_ack == tp->snd_max) { callout_stop(tp->tt_rexmt); needoutput = 1; } else if (!callout_active(tp->tt_persist)) callout_reset(tp->tt_rexmt, tp->t_rxtcur, tcp_timer_rexmt, tp); /* * If no data (only SYN) was ACK'd, * skip rest of ACK processing. */ if (acked == 0) goto step6; /* * When new data is acked, open the congestion window. * If the window gives us less than ssthresh packets * in flight, open exponentially (maxseg per packet). * Otherwise open linearly: maxseg per window * (maxseg^2 / cwnd per packet). */ { register u_int cw = tp->snd_cwnd; register u_int incr = tp->t_maxseg; if (cw > tp->snd_ssthresh) incr = incr * incr / cw; if (tcp_do_newreno == 0 || SEQ_GEQ(th->th_ack, tp->snd_recover)) tp->snd_cwnd = min(cw + incr,TCP_MAXWIN<snd_scale); } if (acked > so->so_snd.sb_cc) { tp->snd_wnd -= so->so_snd.sb_cc; sbdrop(&so->so_snd, (int)so->so_snd.sb_cc); ourfinisacked = 1; } else { sbdrop(&so->so_snd, acked); tp->snd_wnd -= acked; ourfinisacked = 0; } sowwakeup(so); tp->snd_una = th->th_ack; if (SEQ_LT(tp->snd_nxt, tp->snd_una)) tp->snd_nxt = tp->snd_una; switch (tp->t_state) { /* * In FIN_WAIT_1 STATE in addition to the processing * for the ESTABLISHED state if our FIN is now acknowledged * then enter FIN_WAIT_2. */ case TCPS_FIN_WAIT_1: if (ourfinisacked) { /* * If we can't receive any more * data, then closing user can proceed. * Starting the timer is contrary to the * specification, but if we don't get a FIN * we'll hang forever. */ if (so->so_state & SS_CANTRCVMORE) { soisdisconnected(so); callout_reset(tp->tt_2msl, tcp_maxidle, tcp_timer_2msl, tp); } tp->t_state = TCPS_FIN_WAIT_2; } break; /* * In CLOSING STATE in addition to the processing for * the ESTABLISHED state if the ACK acknowledges our FIN * then enter the TIME-WAIT state, otherwise ignore * the segment. */ case TCPS_CLOSING: if (ourfinisacked) { tp->t_state = TCPS_TIME_WAIT; tcp_canceltimers(tp); /* Shorten TIME_WAIT [RFC-1644, p.28] */ if (tp->cc_recv != 0 && (ticks - tp->t_starttime) < tcp_msl) callout_reset(tp->tt_2msl, tp->t_rxtcur * TCPTV_TWTRUNC, tcp_timer_2msl, tp); else callout_reset(tp->tt_2msl, 2 * tcp_msl, tcp_timer_2msl, tp); soisdisconnected(so); } break; /* * In LAST_ACK, we may still be waiting for data to drain * and/or to be acked, as well as for the ack of our FIN. * If our FIN is now acknowledged, delete the TCB, * enter the closed state and return. */ case TCPS_LAST_ACK: if (ourfinisacked) { tp = tcp_close(tp); goto drop; } break; /* * In TIME_WAIT state the only thing that should arrive * is a retransmission of the remote FIN. Acknowledge * it and restart the finack timer. */ case TCPS_TIME_WAIT: callout_reset(tp->tt_2msl, 2 * tcp_msl, tcp_timer_2msl, tp); goto dropafterack; } } step6: /* * Update window information. * Don't look at window if no ACK: TAC's send garbage on first SYN. */ if ((thflags & TH_ACK) && (SEQ_LT(tp->snd_wl1, th->th_seq) || (tp->snd_wl1 == th->th_seq && (SEQ_LT(tp->snd_wl2, th->th_ack) || (tp->snd_wl2 == th->th_ack && tiwin > tp->snd_wnd))))) { /* keep track of pure window updates */ if (tlen == 0 && tp->snd_wl2 == th->th_ack && tiwin > tp->snd_wnd) tcpstat.tcps_rcvwinupd++; tp->snd_wnd = tiwin; tp->snd_wl1 = th->th_seq; tp->snd_wl2 = th->th_ack; if (tp->snd_wnd > tp->max_sndwnd) tp->max_sndwnd = tp->snd_wnd; needoutput = 1; } /* * Process segments with URG. */ if ((thflags & TH_URG) && th->th_urp && TCPS_HAVERCVDFIN(tp->t_state) == 0) { /* * This is a kludge, but if we receive and accept * random urgent pointers, we'll crash in * soreceive. It's hard to imagine someone * actually wanting to send this much urgent data. */ if (th->th_urp + so->so_rcv.sb_cc > sb_max) { th->th_urp = 0; /* XXX */ thflags &= ~TH_URG; /* XXX */ goto dodata; /* XXX */ } /* * If this segment advances the known urgent pointer, * then mark the data stream. This should not happen * in CLOSE_WAIT, CLOSING, LAST_ACK or TIME_WAIT STATES since * a FIN has been received from the remote side. * In these states we ignore the URG. * * According to RFC961 (Assigned Protocols), * the urgent pointer points to the last octet * of urgent data. We continue, however, * to consider it to indicate the first octet * of data past the urgent section as the original * spec states (in one of two places). */ if (SEQ_GT(th->th_seq+th->th_urp, tp->rcv_up)) { tp->rcv_up = th->th_seq + th->th_urp; so->so_oobmark = so->so_rcv.sb_cc + (tp->rcv_up - tp->rcv_nxt) - 1; if (so->so_oobmark == 0) so->so_state |= SS_RCVATMARK; sohasoutofband(so); tp->t_oobflags &= ~(TCPOOB_HAVEDATA | TCPOOB_HADDATA); } /* * Remove out of band data so doesn't get presented to user. * This can happen independent of advancing the URG pointer, * but if two URG's are pending at once, some out-of-band * data may creep in... ick. */ if (th->th_urp <= (u_long)tlen #ifdef SO_OOBINLINE && (so->so_options & SO_OOBINLINE) == 0 #endif ) tcp_pulloutofband(so, th, m, drop_hdrlen); /* hdr drop is delayed */ } else /* * If no out of band data is expected, * pull receive urgent pointer along * with the receive window. */ if (SEQ_GT(tp->rcv_nxt, tp->rcv_up)) tp->rcv_up = tp->rcv_nxt; dodata: /* XXX */ /* * Process the segment text, merging it into the TCP sequencing queue, * and arranging for acknowledgment of receipt if necessary. * This process logically involves adjusting tp->rcv_wnd as data * is presented to the user (this happens in tcp_usrreq.c, * case PRU_RCVD). If a FIN has already been received on this * connection then we just ignore the text. */ if ((tlen || (thflags&TH_FIN)) && TCPS_HAVERCVDFIN(tp->t_state) == 0) { m_adj(m, drop_hdrlen); /* delayed header drop */ TCP_REASS(tp, th, &tlen, m, so, thflags); /* * Note the amount of data that peer has sent into * our window, in order to estimate the sender's * buffer size. */ len = so->so_rcv.sb_hiwat - (tp->rcv_adv - tp->rcv_nxt); } else { m_freem(m); thflags &= ~TH_FIN; } /* * If FIN is received ACK the FIN and let the user know * that the connection is closing. */ if (thflags & TH_FIN) { if (TCPS_HAVERCVDFIN(tp->t_state) == 0) { socantrcvmore(so); /* * If connection is half-synchronized * (ie NEEDSYN flag on) then delay ACK, * so it may be piggybacked when SYN is sent. * Otherwise, since we received a FIN then no * more input can be expected, send ACK now. */ if (tcp_delack_enabled && (tp->t_flags & TF_NEEDSYN)) callout_reset(tp->tt_delack, tcp_delacktime, tcp_timer_delack, tp); else tp->t_flags |= TF_ACKNOW; tp->rcv_nxt++; } switch (tp->t_state) { /* * In SYN_RECEIVED and ESTABLISHED STATES * enter the CLOSE_WAIT state. */ case TCPS_SYN_RECEIVED: tp->t_starttime = ticks; /*FALLTHROUGH*/ case TCPS_ESTABLISHED: tp->t_state = TCPS_CLOSE_WAIT; break; /* * If still in FIN_WAIT_1 STATE FIN has not been acked so * enter the CLOSING state. */ case TCPS_FIN_WAIT_1: tp->t_state = TCPS_CLOSING; break; /* * In FIN_WAIT_2 state enter the TIME_WAIT state, * starting the time-wait timer, turning off the other * standard timers. */ case TCPS_FIN_WAIT_2: tp->t_state = TCPS_TIME_WAIT; tcp_canceltimers(tp); /* Shorten TIME_WAIT [RFC-1644, p.28] */ if (tp->cc_recv != 0 && (ticks - tp->t_starttime) < tcp_msl) { callout_reset(tp->tt_2msl, tp->t_rxtcur * TCPTV_TWTRUNC, tcp_timer_2msl, tp); /* For transaction client, force ACK now. */ tp->t_flags |= TF_ACKNOW; } else callout_reset(tp->tt_2msl, 2 * tcp_msl, tcp_timer_2msl, tp); soisdisconnected(so); break; /* * In TIME_WAIT state restart the 2 MSL time_wait timer. */ case TCPS_TIME_WAIT: callout_reset(tp->tt_2msl, 2 * tcp_msl, tcp_timer_2msl, tp); break; } } #ifdef TCPDEBUG if (so->so_options & SO_DEBUG) tcp_trace(TA_INPUT, ostate, tp, (void *)tcp_saveipgen, &tcp_savetcp, 0); #endif /* * Return any desired output. */ if (needoutput || (tp->t_flags & TF_ACKNOW)) (void) tcp_output(tp); return; dropafterack: /* * Generate an ACK dropping incoming segment if it occupies * sequence space, where the ACK reflects our state. * * We can now skip the test for the RST flag since all * paths to this code happen after packets containing * RST have been dropped. * * In the SYN-RECEIVED state, don't send an ACK unless the * segment we received passes the SYN-RECEIVED ACK test. * If it fails send a RST. This breaks the loop in the * "LAND" DoS attack, and also prevents an ACK storm * between two listening ports that have been sent forged * SYN segments, each with the source address of the other. */ if (tp->t_state == TCPS_SYN_RECEIVED && (thflags & TH_ACK) && (SEQ_GT(tp->snd_una, th->th_ack) || SEQ_GT(th->th_ack, tp->snd_max)) ) goto maybedropwithreset; #ifdef TCPDEBUG if (so->so_options & SO_DEBUG) tcp_trace(TA_DROP, ostate, tp, (void *)tcp_saveipgen, &tcp_savetcp, 0); #endif m_freem(m); tp->t_flags |= TF_ACKNOW; (void) tcp_output(tp); return; /* * Conditionally drop with reset or just drop depending on whether * we think we are under attack or not. */ maybedropwithreset: if (badport_bandlim(1) < 0) goto drop; /* fall through */ dropwithreset: #ifdef TCP_RESTRICT_RST if (restrict_rst) goto drop; #endif /* * Generate a RST, dropping incoming segment. * Make ACK acceptable to originator of segment. * Don't bother to respond if destination was broadcast/multicast. */ if ((thflags & TH_RST) || m->m_flags & (M_BCAST|M_MCAST)) goto drop; #ifdef INET6 if (isipv6) { if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) || IN6_IS_ADDR_MULTICAST(&ip6->ip6_src)) goto drop; } else #endif /* INET6 */ if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr)) || IN_MULTICAST(ntohl(ip->ip_src.s_addr)) || ip->ip_src.s_addr == htonl(INADDR_BROADCAST)) goto drop; /* IPv6 anycast check is done at tcp6_input() */ #ifdef TCPDEBUG if (tp == 0 || (tp->t_inpcb->inp_socket->so_options & SO_DEBUG)) tcp_trace(TA_DROP, ostate, tp, (void *)tcp_saveipgen, &tcp_savetcp, 0); #endif if (thflags & TH_ACK) /* mtod() below is safe as long as hdr dropping is delayed */ tcp_respond(tp, mtod(m, void *), th, m, (tcp_seq)0, th->th_ack, TH_RST); else { if (thflags & TH_SYN) tlen++; /* mtod() below is safe as long as hdr dropping is delayed */ tcp_respond(tp, mtod(m, void *), th, m, th->th_seq+tlen, (tcp_seq)0, TH_RST|TH_ACK); } /* destroy temporarily created socket */ if (dropsocket) (void) soabort(so); return; drop: /* * Drop space held by incoming segment and return. */ #ifdef TCPDEBUG if (tp == 0 || (tp->t_inpcb->inp_socket->so_options & SO_DEBUG)) tcp_trace(TA_DROP, ostate, tp, (void *)tcp_saveipgen, &tcp_savetcp, 0); #endif m_freem(m); /* destroy temporarily created socket */ if (dropsocket) (void) soabort(so); return; } static void tcp_dooptions(tp, cp, cnt, th, to) struct tcpcb *tp; u_char *cp; int cnt; struct tcphdr *th; struct tcpopt *to; { u_short mss = 0; int opt, optlen; for (; cnt > 0; cnt -= optlen, cp += optlen) { opt = cp[0]; if (opt == TCPOPT_EOL) break; if (opt == TCPOPT_NOP) optlen = 1; else { optlen = cp[1]; if (optlen <= 0) break; } switch (opt) { default: continue; case TCPOPT_MAXSEG: if (optlen != TCPOLEN_MAXSEG) continue; if (!(th->th_flags & TH_SYN)) continue; bcopy((char *) cp + 2, (char *) &mss, sizeof(mss)); NTOHS(mss); break; case TCPOPT_WINDOW: if (optlen != TCPOLEN_WINDOW) continue; if (!(th->th_flags & TH_SYN)) continue; tp->t_flags |= TF_RCVD_SCALE; tp->requested_s_scale = min(cp[2], TCP_MAX_WINSHIFT); break; case TCPOPT_TIMESTAMP: if (optlen != TCPOLEN_TIMESTAMP) continue; to->to_flag |= TOF_TS; bcopy((char *)cp + 2, (char *)&to->to_tsval, sizeof(to->to_tsval)); NTOHL(to->to_tsval); bcopy((char *)cp + 6, (char *)&to->to_tsecr, sizeof(to->to_tsecr)); NTOHL(to->to_tsecr); /* * A timestamp received in a SYN makes * it ok to send timestamp requests and replies. */ if (th->th_flags & TH_SYN) { tp->t_flags |= TF_RCVD_TSTMP; tp->ts_recent = to->to_tsval; tp->ts_recent_age = ticks; } break; case TCPOPT_CC: if (optlen != TCPOLEN_CC) continue; to->to_flag |= TOF_CC; bcopy((char *)cp + 2, (char *)&to->to_cc, sizeof(to->to_cc)); NTOHL(to->to_cc); /* * A CC or CC.new option received in a SYN makes * it ok to send CC in subsequent segments. */ if (th->th_flags & TH_SYN) tp->t_flags |= TF_RCVD_CC; break; case TCPOPT_CCNEW: if (optlen != TCPOLEN_CC) continue; if (!(th->th_flags & TH_SYN)) continue; to->to_flag |= TOF_CCNEW; bcopy((char *)cp + 2, (char *)&to->to_cc, sizeof(to->to_cc)); NTOHL(to->to_cc); /* * A CC or CC.new option received in a SYN makes * it ok to send CC in subsequent segments. */ tp->t_flags |= TF_RCVD_CC; break; case TCPOPT_CCECHO: if (optlen != TCPOLEN_CC) continue; if (!(th->th_flags & TH_SYN)) continue; to->to_flag |= TOF_CCECHO; bcopy((char *)cp + 2, (char *)&to->to_ccecho, sizeof(to->to_ccecho)); NTOHL(to->to_ccecho); break; } } if (th->th_flags & TH_SYN) tcp_mss(tp, mss); /* sets t_maxseg */ } /* * Pull out of band byte out of a segment so * it doesn't appear in the user's data queue. * It is still reflected in the segment length for * sequencing purposes. */ static void tcp_pulloutofband(so, th, m, off) struct socket *so; struct tcphdr *th; register struct mbuf *m; int off; /* delayed to be droped hdrlen */ { int cnt = off + th->th_urp - 1; while (cnt >= 0) { if (m->m_len > cnt) { char *cp = mtod(m, caddr_t) + cnt; struct tcpcb *tp = sototcpcb(so); tp->t_iobc = *cp; tp->t_oobflags |= TCPOOB_HAVEDATA; bcopy(cp+1, cp, (unsigned)(m->m_len - cnt - 1)); m->m_len--; if (m->m_flags & M_PKTHDR) m->m_pkthdr.len--; return; } cnt -= m->m_len; m = m->m_next; if (m == 0) break; } panic("tcp_pulloutofband"); } /* * Collect new round-trip time estimate * and update averages and current timeout. */ static void tcp_xmit_timer(tp, rtt) register struct tcpcb *tp; int rtt; { register int delta; tcpstat.tcps_rttupdated++; tp->t_rttupdated++; if (tp->t_srtt != 0) { /* * srtt is stored as fixed point with 5 bits after the * binary point (i.e., scaled by 8). The following magic * is equivalent to the smoothing algorithm in rfc793 with * an alpha of .875 (srtt = rtt/8 + srtt*7/8 in fixed * point). Adjust rtt to origin 0. */ delta = ((rtt - 1) << TCP_DELTA_SHIFT) - (tp->t_srtt >> (TCP_RTT_SHIFT - TCP_DELTA_SHIFT)); if ((tp->t_srtt += delta) <= 0) tp->t_srtt = 1; /* * We accumulate a smoothed rtt variance (actually, a * smoothed mean difference), then set the retransmit * timer to smoothed rtt + 4 times the smoothed variance. * rttvar is stored as fixed point with 4 bits after the * binary point (scaled by 16). The following is * equivalent to rfc793 smoothing with an alpha of .75 * (rttvar = rttvar*3/4 + |delta| / 4). This replaces * rfc793's wired-in beta. */ if (delta < 0) delta = -delta; delta -= tp->t_rttvar >> (TCP_RTTVAR_SHIFT - TCP_DELTA_SHIFT); if ((tp->t_rttvar += delta) <= 0) tp->t_rttvar = 1; } else { /* * No rtt measurement yet - use the unsmoothed rtt. * Set the variance to half the rtt (so our first * retransmit happens at 3*rtt). */ tp->t_srtt = rtt << TCP_RTT_SHIFT; tp->t_rttvar = rtt << (TCP_RTTVAR_SHIFT - 1); } tp->t_rtttime = 0; tp->t_rxtshift = 0; /* * the retransmit should happen at rtt + 4 * rttvar. * Because of the way we do the smoothing, srtt and rttvar * will each average +1/2 tick of bias. When we compute * the retransmit timer, we want 1/2 tick of rounding and * 1 extra tick because of +-1/2 tick uncertainty in the * firing of the timer. The bias will give us exactly the * 1.5 tick we need. But, because the bias is * statistical, we have to test that we don't drop below * the minimum feasible timer (which is 2 ticks). */ TCPT_RANGESET(tp->t_rxtcur, TCP_REXMTVAL(tp), max(tp->t_rttmin, rtt + 2), TCPTV_REXMTMAX); /* * We received an ack for a packet that wasn't retransmitted; * it is probably safe to discard any error indications we've * received recently. This isn't quite right, but close enough * for now (a route might have failed after we sent a segment, * and the return path might not be symmetrical). */ tp->t_softerror = 0; } /* * Determine a reasonable value for maxseg size. * If the route is known, check route for mtu. * If none, use an mss that can be handled on the outgoing * interface without forcing IP to fragment; if bigger than * an mbuf cluster (MCLBYTES), round down to nearest multiple of MCLBYTES * to utilize large mbufs. If no route is found, route has no mtu, * or the destination isn't local, use a default, hopefully conservative * size (usually 512 or the default IP max size, but no more than the mtu * of the interface), as we can't discover anything about intervening * gateways or networks. We also initialize the congestion/slow start * window to be a single segment if the destination isn't local. * While looking at the routing entry, we also initialize other path-dependent * parameters from pre-set or cached values in the routing entry. * * Also take into account the space needed for options that we * send regularly. Make maxseg shorter by that amount to assure * that we can send maxseg amount of data even when the options * are present. Store the upper limit of the length of options plus * data in maxopd. * * NOTE that this routine is only called when we process an incoming * segment, for outgoing segments only tcp_mssopt is called. * * In case of T/TCP, we call this routine during implicit connection * setup as well (offer = -1), to initialize maxseg from the cached * MSS of our peer. */ void tcp_mss(tp, offer) struct tcpcb *tp; int offer; { register struct rtentry *rt; struct ifnet *ifp; register int rtt, mss; u_long bufsize; struct inpcb *inp; struct socket *so; struct rmxp_tao *taop; int origoffer = offer; #ifdef INET6 int isipv6; int min_protoh; #endif inp = tp->t_inpcb; #ifdef INET6 isipv6 = ((inp->inp_vflag & INP_IPV6) != 0) ? 1 : 0; min_protoh = isipv6 ? sizeof (struct ip6_hdr) + sizeof (struct tcphdr) : sizeof (struct tcpiphdr); #else #define min_protoh (sizeof (struct tcpiphdr)) #endif #ifdef INET6 if (isipv6) rt = tcp_rtlookup6(inp); else #endif rt = tcp_rtlookup(inp); if (rt == NULL) { tp->t_maxopd = tp->t_maxseg = #ifdef INET6 isipv6 ? tcp_v6mssdflt : #endif /* INET6 */ tcp_mssdflt; return; } ifp = rt->rt_ifp; so = inp->inp_socket; taop = rmx_taop(rt->rt_rmx); /* * Offer == -1 means that we didn't receive SYN yet, * use cached value in that case; */ if (offer == -1) offer = taop->tao_mssopt; /* * Offer == 0 means that there was no MSS on the SYN segment, * in this case we use tcp_mssdflt. */ if (offer == 0) offer = #ifdef INET6 isipv6 ? tcp_v6mssdflt : #endif /* INET6 */ tcp_mssdflt; else /* * Sanity check: make sure that maxopd will be large * enough to allow some data on segments even is the * all the option space is used (40bytes). Otherwise * funny things may happen in tcp_output. */ offer = max(offer, 64); taop->tao_mssopt = offer; /* * While we're here, check if there's an initial rtt * or rttvar. Convert from the route-table units * to scaled multiples of the slow timeout timer. */ if (tp->t_srtt == 0 && (rtt = rt->rt_rmx.rmx_rtt)) { /* * XXX the lock bit for RTT indicates that the value * is also a minimum value; this is subject to time. */ if (rt->rt_rmx.rmx_locks & RTV_RTT) tp->t_rttmin = rtt / (RTM_RTTUNIT / hz); tp->t_srtt = rtt / (RTM_RTTUNIT / (hz * TCP_RTT_SCALE)); tcpstat.tcps_usedrtt++; if (rt->rt_rmx.rmx_rttvar) { tp->t_rttvar = rt->rt_rmx.rmx_rttvar / (RTM_RTTUNIT / (hz * TCP_RTTVAR_SCALE)); tcpstat.tcps_usedrttvar++; } else { /* default variation is +- 1 rtt */ tp->t_rttvar = tp->t_srtt * TCP_RTTVAR_SCALE / TCP_RTT_SCALE; } TCPT_RANGESET(tp->t_rxtcur, ((tp->t_srtt >> 2) + tp->t_rttvar) >> 1, tp->t_rttmin, TCPTV_REXMTMAX); } /* * if there's an mtu associated with the route, use it * else, use the link mtu. */ if (rt->rt_rmx.rmx_mtu) mss = rt->rt_rmx.rmx_mtu - min_protoh; else { mss = #ifdef INET6 (isipv6 ? nd_ifinfo[rt->rt_ifp->if_index].linkmtu : #endif ifp->if_mtu #ifdef INET6 ) #endif - min_protoh; #ifdef INET6 if (isipv6) { if (!in6_localaddr(&inp->in6p_faddr)) mss = min(mss, tcp_v6mssdflt); } else #endif if (!in_localaddr(inp->inp_faddr)) mss = min(mss, tcp_mssdflt); } mss = min(mss, offer); /* * maxopd stores the maximum length of data AND options * in a segment; maxseg is the amount of data in a normal * segment. We need to store this value (maxopd) apart * from maxseg, because now every segment carries options * and thus we normally have somewhat less data in segments. */ tp->t_maxopd = mss; /* * In case of T/TCP, origoffer==-1 indicates, that no segments * were received yet. In this case we just guess, otherwise * we do the same as before T/TCP. */ if ((tp->t_flags & (TF_REQ_TSTMP|TF_NOOPT)) == TF_REQ_TSTMP && (origoffer == -1 || (tp->t_flags & TF_RCVD_TSTMP) == TF_RCVD_TSTMP)) mss -= TCPOLEN_TSTAMP_APPA; if ((tp->t_flags & (TF_REQ_CC|TF_NOOPT)) == TF_REQ_CC && (origoffer == -1 || (tp->t_flags & TF_RCVD_CC) == TF_RCVD_CC)) mss -= TCPOLEN_CC_APPA; #if (MCLBYTES & (MCLBYTES - 1)) == 0 if (mss > MCLBYTES) mss &= ~(MCLBYTES-1); #else if (mss > MCLBYTES) mss = mss / MCLBYTES * MCLBYTES; #endif /* * If there's a pipesize, change the socket buffer * to that size. Make the socket buffers an integral * number of mss units; if the mss is larger than * the socket buffer, decrease the mss. */ #ifdef RTV_SPIPE if ((bufsize = rt->rt_rmx.rmx_sendpipe) == 0) #endif bufsize = so->so_snd.sb_hiwat; if (bufsize < mss) mss = bufsize; else { bufsize = roundup(bufsize, mss); if (bufsize > sb_max) bufsize = sb_max; (void)sbreserve(&so->so_snd, bufsize, so, NULL); } tp->t_maxseg = mss; #ifdef RTV_RPIPE if ((bufsize = rt->rt_rmx.rmx_recvpipe) == 0) #endif bufsize = so->so_rcv.sb_hiwat; if (bufsize > mss) { bufsize = roundup(bufsize, mss); if (bufsize > sb_max) bufsize = sb_max; (void)sbreserve(&so->so_rcv, bufsize, so, NULL); } /* * Set the slow-start flight size depending on whether this * is a local network or not. */ if ( #ifdef INET6 (isipv6 && in6_localaddr(&inp->in6p_faddr)) || (!isipv6 && #endif in_localaddr(inp->inp_faddr) #ifdef INET6 ) #endif ) tp->snd_cwnd = mss * ss_fltsz_local; else tp->snd_cwnd = mss * ss_fltsz; if (rt->rt_rmx.rmx_ssthresh) { /* * There's some sort of gateway or interface * buffer limit on the path. Use this to set * the slow start threshhold, but set the * threshold to no less than 2*mss. */ tp->snd_ssthresh = max(2 * mss, rt->rt_rmx.rmx_ssthresh); tcpstat.tcps_usedssthresh++; } } /* * Determine the MSS option to send on an outgoing SYN. */ int tcp_mssopt(tp) struct tcpcb *tp; { struct rtentry *rt; #ifdef INET6 int isipv6; int min_protoh; #endif #ifdef INET6 isipv6 = ((tp->t_inpcb->inp_vflag & INP_IPV6) != 0) ? 1 : 0; min_protoh = isipv6 ? sizeof (struct ip6_hdr) + sizeof (struct tcphdr) : sizeof (struct tcpiphdr); #else #define min_protoh (sizeof (struct tcpiphdr)) #endif #ifdef INET6 if (isipv6) rt = tcp_rtlookup6(tp->t_inpcb); else #endif /* INET6 */ rt = tcp_rtlookup(tp->t_inpcb); if (rt == NULL) return #ifdef INET6 isipv6 ? tcp_v6mssdflt : #endif /* INET6 */ tcp_mssdflt; return rt->rt_ifp->if_mtu - min_protoh; } /* * Checks for partial ack. If partial ack arrives, force the retransmission * of the next unacknowledged segment, do not clear tp->t_dupacks, and return * 1. By setting snd_nxt to ti_ack, this forces retransmission timer to * be started again. If the ack advances at least to tp->snd_recover, return 0. */ static int tcp_newreno(tp, th) struct tcpcb *tp; struct tcphdr *th; { if (SEQ_LT(th->th_ack, tp->snd_recover)) { tcp_seq onxt = tp->snd_nxt; u_long ocwnd = tp->snd_cwnd; callout_stop(tp->tt_rexmt); tp->t_rtttime = 0; tp->snd_nxt = th->th_ack; /* * Set snd_cwnd to one segment beyond acknowledged offset * (tp->snd_una has not yet been updated when this function * is called) */ tp->snd_cwnd = tp->t_maxseg + (th->th_ack - tp->snd_una); (void) tcp_output(tp); tp->snd_cwnd = ocwnd; if (SEQ_GT(onxt, tp->snd_nxt)) tp->snd_nxt = onxt; /* * Partial window deflation. Relies on fact that tp->snd_una * not updated yet. */ tp->snd_cwnd -= (th->th_ack - tp->snd_una - tp->t_maxseg); return (1); } return (0); } Index: head/sys/netinet/tcp_subr.c =================================================================== --- head/sys/netinet/tcp_subr.c (revision 62586) +++ head/sys/netinet/tcp_subr.c (revision 62587) @@ -1,1301 +1,1297 @@ /* * Copyright (c) 1982, 1986, 1988, 1990, 1993, 1995 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)tcp_subr.c 8.2 (Berkeley) 5/24/95 * $FreeBSD$ */ #include "opt_compat.h" #include "opt_inet6.h" #include "opt_ipsec.h" #include "opt_tcpdebug.h" #include #include #include #include #include #include #include #include #ifdef INET6 #include #endif #include #include #include #include #include #include #include #define _IP_VHL #include #include #include #ifdef INET6 #include #endif #include #ifdef INET6 #include #endif #include #include #ifdef INET6 #include #endif #include #include #include #include #include #ifdef INET6 #include #endif #include #ifdef TCPDEBUG #include #endif #include #ifdef IPSEC #include +#ifdef INET6 +#include +#endif #endif /*IPSEC*/ #include int tcp_mssdflt = TCP_MSS; SYSCTL_INT(_net_inet_tcp, TCPCTL_MSSDFLT, mssdflt, CTLFLAG_RW, &tcp_mssdflt , 0, "Default TCP Maximum Segment Size"); #ifdef INET6 int tcp_v6mssdflt = TCP6_MSS; SYSCTL_INT(_net_inet_tcp, TCPCTL_V6MSSDFLT, v6mssdflt, CTLFLAG_RW, &tcp_v6mssdflt , 0, "Default TCP Maximum Segment Size for IPv6"); #endif #if 0 static int tcp_rttdflt = TCPTV_SRTTDFLT / PR_SLOWHZ; SYSCTL_INT(_net_inet_tcp, TCPCTL_RTTDFLT, rttdflt, CTLFLAG_RW, &tcp_rttdflt , 0, "Default maximum TCP Round Trip Time"); #endif static int tcp_do_rfc1323 = 1; SYSCTL_INT(_net_inet_tcp, TCPCTL_DO_RFC1323, rfc1323, CTLFLAG_RW, &tcp_do_rfc1323 , 0, "Enable rfc1323 (high performance TCP) extensions"); static int tcp_do_rfc1644 = 0; SYSCTL_INT(_net_inet_tcp, TCPCTL_DO_RFC1644, rfc1644, CTLFLAG_RW, &tcp_do_rfc1644 , 0, "Enable rfc1644 (TTCP) extensions"); static int tcp_tcbhashsize = 0; SYSCTL_INT(_net_inet_tcp, OID_AUTO, tcbhashsize, CTLFLAG_RD, &tcp_tcbhashsize, 0, "Size of TCP control-block hashtable"); static int do_tcpdrain = 1; SYSCTL_INT(_debug, OID_AUTO, do_tcpdrain, CTLFLAG_RW, &do_tcpdrain, 0, "Enable non Net3 compliant tcp_drain"); SYSCTL_INT(_net_inet_tcp, OID_AUTO, pcbcount, CTLFLAG_RD, &tcbinfo.ipi_count, 0, "Number of active PCBs"); static void tcp_cleartaocache __P((void)); static void tcp_notify __P((struct inpcb *, int)); /* * Target size of TCP PCB hash tables. Must be a power of two. * * Note that this can be overridden by the kernel environment * variable net.inet.tcp.tcbhashsize */ #ifndef TCBHASHSIZE #define TCBHASHSIZE 512 #endif /* * This is the actual shape of what we allocate using the zone * allocator. Doing it this way allows us to protect both structures * using the same generation count, and also eliminates the overhead * of allocating tcpcbs separately. By hiding the structure here, * we avoid changing most of the rest of the code (although it needs * to be changed, eventually, for greater efficiency). */ #define ALIGNMENT 32 #define ALIGNM1 (ALIGNMENT - 1) struct inp_tp { union { struct inpcb inp; char align[(sizeof(struct inpcb) + ALIGNM1) & ~ALIGNM1]; } inp_tp_u; struct tcpcb tcb; struct callout inp_tp_rexmt, inp_tp_persist, inp_tp_keep, inp_tp_2msl; struct callout inp_tp_delack; }; #undef ALIGNMENT #undef ALIGNM1 /* * Tcp initialization */ void tcp_init() { int hashsize; tcp_iss = random(); /* wrong, but better than a constant */ tcp_ccgen = 1; tcp_cleartaocache(); tcp_delacktime = TCPTV_DELACK; tcp_keepinit = TCPTV_KEEP_INIT; tcp_keepidle = TCPTV_KEEP_IDLE; tcp_keepintvl = TCPTV_KEEPINTVL; tcp_maxpersistidle = TCPTV_KEEP_IDLE; tcp_msl = TCPTV_MSL; LIST_INIT(&tcb); tcbinfo.listhead = &tcb; TUNABLE_INT_FETCH("net.inet.tcp.tcbhashsize", TCBHASHSIZE, hashsize); if (!powerof2(hashsize)) { printf("WARNING: TCB hash size not a power of 2\n"); hashsize = 512; /* safe default */ } tcp_tcbhashsize = hashsize; tcbinfo.hashbase = hashinit(hashsize, M_PCB, &tcbinfo.hashmask); tcbinfo.porthashbase = hashinit(hashsize, M_PCB, &tcbinfo.porthashmask); tcbinfo.ipi_zone = zinit("tcpcb", sizeof(struct inp_tp), maxsockets, ZONE_INTERRUPT, 0); #ifdef INET6 #define TCP_MINPROTOHDR (sizeof(struct ip6_hdr) + sizeof(struct tcphdr)) #else /* INET6 */ #define TCP_MINPROTOHDR (sizeof(struct tcpiphdr)) #endif /* INET6 */ if (max_protohdr < TCP_MINPROTOHDR) max_protohdr = TCP_MINPROTOHDR; if (max_linkhdr + TCP_MINPROTOHDR > MHLEN) panic("tcp_init"); #undef TCP_MINPROTOHDR } /* * Create template to be used to send tcp packets on a connection. * Call after host entry created, allocates an mbuf and fills * in a skeletal tcp/ip header, minimizing the amount of work * necessary when the connection is used. */ struct tcptemp * tcp_template(tp) struct tcpcb *tp; { register struct inpcb *inp = tp->t_inpcb; register struct mbuf *m; register struct tcptemp *n; if ((n = tp->t_template) == 0) { m = m_get(M_DONTWAIT, MT_HEADER); if (m == NULL) return (0); m->m_len = sizeof (struct tcptemp); n = mtod(m, struct tcptemp *); } #ifdef INET6 if ((inp->inp_vflag & INP_IPV6) != 0) { register struct ip6_hdr *ip6; ip6 = (struct ip6_hdr *)n->tt_ipgen; ip6->ip6_flow = (ip6->ip6_flow & ~IPV6_FLOWINFO_MASK) | (inp->in6p_flowinfo & IPV6_FLOWINFO_MASK); ip6->ip6_vfc = (ip6->ip6_vfc & ~IPV6_VERSION_MASK) | (IPV6_VERSION & IPV6_VERSION_MASK); ip6->ip6_nxt = IPPROTO_TCP; ip6->ip6_plen = sizeof(struct tcphdr); ip6->ip6_src = inp->in6p_laddr; ip6->ip6_dst = inp->in6p_faddr; n->tt_t.th_sum = 0; } else #endif { struct ip *ip = (struct ip *)n->tt_ipgen; bzero(ip, sizeof(struct ip)); /* XXX overkill? */ ip->ip_vhl = IP_VHL_BORING; ip->ip_p = IPPROTO_TCP; ip->ip_src = inp->inp_laddr; ip->ip_dst = inp->inp_faddr; n->tt_t.th_sum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr, htons(sizeof(struct tcphdr) + IPPROTO_TCP)); } n->tt_t.th_sport = inp->inp_lport; n->tt_t.th_dport = inp->inp_fport; n->tt_t.th_seq = 0; n->tt_t.th_ack = 0; n->tt_t.th_x2 = 0; n->tt_t.th_off = 5; n->tt_t.th_flags = 0; n->tt_t.th_win = 0; n->tt_t.th_urp = 0; return (n); } /* * Send a single message to the TCP at address specified by * the given TCP/IP header. If m == 0, then we make a copy * of the tcpiphdr at ti and send directly to the addressed host. * This is used to force keep alive messages out using the TCP * template for a connection tp->t_template. If flags are given * then we send a message back to the TCP which originated the * segment ti, and discard the mbuf containing it and any other * attached mbufs. * * In any case the ack and sequence number of the transmitted * segment are as specified by the parameters. * * NOTE: If m != NULL, then ti must point to *inside* the mbuf. */ void tcp_respond(tp, ipgen, th, m, ack, seq, flags) struct tcpcb *tp; void *ipgen; register struct tcphdr *th; register struct mbuf *m; tcp_seq ack, seq; int flags; { register int tlen; int win = 0; struct route *ro = 0; struct route sro; struct ip *ip; struct tcphdr *nth; #ifdef INET6 struct route_in6 *ro6 = 0; struct route_in6 sro6; struct ip6_hdr *ip6; int isipv6; #endif /* INET6 */ int ipflags = 0; #ifdef INET6 isipv6 = IP_VHL_V(((struct ip *)ipgen)->ip_vhl) == 6; ip6 = ipgen; #endif /* INET6 */ ip = ipgen; if (tp) { if (!(flags & TH_RST)) { win = sbspace(&tp->t_inpcb->inp_socket->so_rcv); if (win > (long)TCP_MAXWIN << tp->rcv_scale) win = (long)TCP_MAXWIN << tp->rcv_scale; } #ifdef INET6 if (isipv6) ro6 = &tp->t_inpcb->in6p_route; else #endif /* INET6 */ ro = &tp->t_inpcb->inp_route; } else { #ifdef INET6 if (isipv6) { ro6 = &sro6; bzero(ro6, sizeof *ro6); } else #endif /* INET6 */ { ro = &sro; bzero(ro, sizeof *ro); } } if (m == 0) { m = m_gethdr(M_DONTWAIT, MT_HEADER); if (m == NULL) return; #ifdef TCP_COMPAT_42 tlen = 1; #else tlen = 0; #endif m->m_data += max_linkhdr; #ifdef INET6 if (isipv6) { bcopy((caddr_t)ip6, mtod(m, caddr_t), sizeof(struct ip6_hdr)); ip6 = mtod(m, struct ip6_hdr *); nth = (struct tcphdr *)(ip6 + 1); } else #endif /* INET6 */ { bcopy((caddr_t)ip, mtod(m, caddr_t), sizeof(struct ip)); ip = mtod(m, struct ip *); nth = (struct tcphdr *)(ip + 1); } bcopy((caddr_t)th, (caddr_t)nth, sizeof(struct tcphdr)); flags = TH_ACK; } else { m_freem(m->m_next); m->m_next = 0; m->m_data = (caddr_t)ipgen; /* m_len is set later */ tlen = 0; #define xchg(a,b,type) { type t; t=a; a=b; b=t; } #ifdef INET6 if (isipv6) { xchg(ip6->ip6_dst, ip6->ip6_src, struct in6_addr); nth = (struct tcphdr *)(ip6 + 1); } else #endif /* INET6 */ { xchg(ip->ip_dst.s_addr, ip->ip_src.s_addr, n_long); nth = (struct tcphdr *)(ip + 1); } if (th != nth) { /* * this is usually a case when an extension header * exists between the IPv6 header and the * TCP header. */ nth->th_sport = th->th_sport; nth->th_dport = th->th_dport; } xchg(nth->th_dport, nth->th_sport, n_short); #undef xchg } #ifdef INET6 if (isipv6) { ip6->ip6_plen = htons((u_short)(sizeof (struct tcphdr) + tlen)); tlen += sizeof (struct ip6_hdr) + sizeof (struct tcphdr); } else #endif { tlen += sizeof (struct tcpiphdr); ip->ip_len = tlen; ip->ip_ttl = ip_defttl; } m->m_len = tlen; m->m_pkthdr.len = tlen; m->m_pkthdr.rcvif = (struct ifnet *) 0; nth->th_seq = htonl(seq); nth->th_ack = htonl(ack); nth->th_x2 = 0; nth->th_off = sizeof (struct tcphdr) >> 2; nth->th_flags = flags; if (tp) nth->th_win = htons((u_short) (win >> tp->rcv_scale)); else nth->th_win = htons((u_short)win); nth->th_urp = 0; #ifdef INET6 if (isipv6) { nth->th_sum = 0; nth->th_sum = in6_cksum(m, IPPROTO_TCP, sizeof(struct ip6_hdr), tlen - sizeof(struct ip6_hdr)); ip6->ip6_hlim = in6_selecthlim(tp ? tp->t_inpcb : NULL, ro6 && ro6->ro_rt ? ro6->ro_rt->rt_ifp : NULL); } else #endif /* INET6 */ { nth->th_sum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr, htons((u_short)(tlen - sizeof(struct ip) + ip->ip_p))); m->m_pkthdr.csum_flags = CSUM_TCP; m->m_pkthdr.csum_data = offsetof(struct tcphdr, th_sum); } #ifdef TCPDEBUG if (tp == NULL || (tp->t_inpcb->inp_socket->so_options & SO_DEBUG)) tcp_trace(TA_OUTPUT, 0, tp, mtod(m, void *), th, 0); #endif #ifdef IPSEC - if (tp != NULL) { - m->m_pkthdr.rcvif = (struct ifnet *)tp->t_inpcb->inp_socket; - ipflags |= -#ifdef INET6 - isipv6 ? IPV6_SOCKINMRCVIF : -#endif - IP_SOCKINMRCVIF; - } + ipsec_setsocket(m, tp ? tp->t_inpcb->inp_socket : NULL); #endif #ifdef INET6 if (isipv6) { (void)ip6_output(m, NULL, ro6, ipflags, NULL, NULL); if (ro6 == &sro6 && ro6->ro_rt) { RTFREE(ro6->ro_rt); ro6->ro_rt = NULL; } } else #endif /* INET6 */ { (void) ip_output(m, NULL, ro, ipflags, NULL); if (ro == &sro && ro->ro_rt) { RTFREE(ro->ro_rt); ro->ro_rt = NULL; } } } /* * Create a new TCP control block, making an * empty reassembly queue and hooking it to the argument * protocol control block. The `inp' parameter must have * come from the zone allocator set up in tcp_init(). */ struct tcpcb * tcp_newtcpcb(inp) struct inpcb *inp; { struct inp_tp *it; register struct tcpcb *tp; #ifdef INET6 int isipv6 = (inp->inp_vflag & INP_IPV6) != 0; #endif /* INET6 */ it = (struct inp_tp *)inp; tp = &it->tcb; bzero((char *) tp, sizeof(struct tcpcb)); LIST_INIT(&tp->t_segq); tp->t_maxseg = tp->t_maxopd = #ifdef INET6 isipv6 ? tcp_v6mssdflt : #endif /* INET6 */ tcp_mssdflt; /* Set up our timeouts. */ callout_init(tp->tt_rexmt = &it->inp_tp_rexmt); callout_init(tp->tt_persist = &it->inp_tp_persist); callout_init(tp->tt_keep = &it->inp_tp_keep); callout_init(tp->tt_2msl = &it->inp_tp_2msl); callout_init(tp->tt_delack = &it->inp_tp_delack); if (tcp_do_rfc1323) tp->t_flags = (TF_REQ_SCALE|TF_REQ_TSTMP); if (tcp_do_rfc1644) tp->t_flags |= TF_REQ_CC; tp->t_inpcb = inp; /* XXX */ /* * Init srtt to TCPTV_SRTTBASE (0), so we can tell that we have no * rtt estimate. Set rttvar so that srtt + 4 * rttvar gives * reasonable initial retransmit time. */ tp->t_srtt = TCPTV_SRTTBASE; tp->t_rttvar = ((TCPTV_RTOBASE - TCPTV_SRTTBASE) << TCP_RTTVAR_SHIFT) / 4; tp->t_rttmin = TCPTV_MIN; tp->t_rxtcur = TCPTV_RTOBASE; tp->snd_cwnd = TCP_MAXWIN << TCP_MAX_WINSHIFT; tp->snd_ssthresh = TCP_MAXWIN << TCP_MAX_WINSHIFT; tp->t_rcvtime = ticks; /* * IPv4 TTL initialization is necessary for an IPv6 socket as well, * because the socket may be bound to an IPv6 wildcard address, * which may match an IPv4-mapped IPv6 address. */ inp->inp_ip_ttl = ip_defttl; inp->inp_ppcb = (caddr_t)tp; return (tp); /* XXX */ } /* * Drop a TCP connection, reporting * the specified error. If connection is synchronized, * then send a RST to peer. */ struct tcpcb * tcp_drop(tp, errno) register struct tcpcb *tp; int errno; { struct socket *so = tp->t_inpcb->inp_socket; if (TCPS_HAVERCVDSYN(tp->t_state)) { tp->t_state = TCPS_CLOSED; (void) tcp_output(tp); tcpstat.tcps_drops++; } else tcpstat.tcps_conndrops++; if (errno == ETIMEDOUT && tp->t_softerror) errno = tp->t_softerror; so->so_error = errno; return (tcp_close(tp)); } /* * Close a TCP control block: * discard all space held by the tcp * discard internet protocol block * wake up any sleepers */ struct tcpcb * tcp_close(tp) register struct tcpcb *tp; { register struct tseg_qent *q; struct inpcb *inp = tp->t_inpcb; struct socket *so = inp->inp_socket; #ifdef INET6 int isipv6 = (inp->inp_vflag & INP_IPV6) != 0; #endif /* INET6 */ register struct rtentry *rt; int dosavessthresh; /* * Make sure that all of our timers are stopped before we * delete the PCB. */ callout_stop(tp->tt_rexmt); callout_stop(tp->tt_persist); callout_stop(tp->tt_keep); callout_stop(tp->tt_2msl); callout_stop(tp->tt_delack); /* * If we got enough samples through the srtt filter, * save the rtt and rttvar in the routing entry. * 'Enough' is arbitrarily defined as the 16 samples. * 16 samples is enough for the srtt filter to converge * to within 5% of the correct value; fewer samples and * we could save a very bogus rtt. * * Don't update the default route's characteristics and don't * update anything that the user "locked". */ if (tp->t_rttupdated >= 16) { register u_long i = 0; #ifdef INET6 if (isipv6) { struct sockaddr_in6 *sin6; if ((rt = inp->in6p_route.ro_rt) == NULL) goto no_valid_rt; sin6 = (struct sockaddr_in6 *)rt_key(rt); if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) goto no_valid_rt; } else #endif /* INET6 */ if ((rt = inp->inp_route.ro_rt) == NULL || ((struct sockaddr_in *)rt_key(rt))->sin_addr.s_addr == INADDR_ANY) goto no_valid_rt; if ((rt->rt_rmx.rmx_locks & RTV_RTT) == 0) { i = tp->t_srtt * (RTM_RTTUNIT / (hz * TCP_RTT_SCALE)); if (rt->rt_rmx.rmx_rtt && i) /* * filter this update to half the old & half * the new values, converting scale. * See route.h and tcp_var.h for a * description of the scaling constants. */ rt->rt_rmx.rmx_rtt = (rt->rt_rmx.rmx_rtt + i) / 2; else rt->rt_rmx.rmx_rtt = i; tcpstat.tcps_cachedrtt++; } if ((rt->rt_rmx.rmx_locks & RTV_RTTVAR) == 0) { i = tp->t_rttvar * (RTM_RTTUNIT / (hz * TCP_RTTVAR_SCALE)); if (rt->rt_rmx.rmx_rttvar && i) rt->rt_rmx.rmx_rttvar = (rt->rt_rmx.rmx_rttvar + i) / 2; else rt->rt_rmx.rmx_rttvar = i; tcpstat.tcps_cachedrttvar++; } /* * The old comment here said: * update the pipelimit (ssthresh) if it has been updated * already or if a pipesize was specified & the threshhold * got below half the pipesize. I.e., wait for bad news * before we start updating, then update on both good * and bad news. * * But we want to save the ssthresh even if no pipesize is * specified explicitly in the route, because such * connections still have an implicit pipesize specified * by the global tcp_sendspace. In the absence of a reliable * way to calculate the pipesize, it will have to do. */ i = tp->snd_ssthresh; if (rt->rt_rmx.rmx_sendpipe != 0) dosavessthresh = (i < rt->rt_rmx.rmx_sendpipe / 2); else dosavessthresh = (i < so->so_snd.sb_hiwat / 2); if (((rt->rt_rmx.rmx_locks & RTV_SSTHRESH) == 0 && i != 0 && rt->rt_rmx.rmx_ssthresh != 0) || dosavessthresh) { /* * convert the limit from user data bytes to * packets then to packet data bytes. */ i = (i + tp->t_maxseg / 2) / tp->t_maxseg; if (i < 2) i = 2; i *= (u_long)(tp->t_maxseg + #ifdef INET6 (isipv6 ? sizeof (struct ip6_hdr) + sizeof (struct tcphdr) : #endif sizeof (struct tcpiphdr) #ifdef INET6 ) #endif ); if (rt->rt_rmx.rmx_ssthresh) rt->rt_rmx.rmx_ssthresh = (rt->rt_rmx.rmx_ssthresh + i) / 2; else rt->rt_rmx.rmx_ssthresh = i; tcpstat.tcps_cachedssthresh++; } } no_valid_rt: /* free the reassembly queue, if any */ while((q = LIST_FIRST(&tp->t_segq)) != NULL) { LIST_REMOVE(q, tqe_q); m_freem(q->tqe_m); FREE(q, M_TSEGQ); } if (tp->t_template) (void) m_free(dtom(tp->t_template)); inp->inp_ppcb = NULL; soisdisconnected(so); #ifdef INET6 if (INP_CHECK_SOCKAF(so, AF_INET6)) in6_pcbdetach(inp); else #endif /* INET6 */ in_pcbdetach(inp); tcpstat.tcps_closed++; return ((struct tcpcb *)0); } void tcp_drain() { if (do_tcpdrain) { struct inpcb *inpb; struct tcpcb *tcpb; struct tseg_qent *te; /* * Walk the tcpbs, if existing, and flush the reassembly queue, * if there is one... * XXX: The "Net/3" implementation doesn't imply that the TCP * reassembly queue should be flushed, but in a situation * where we're really low on mbufs, this is potentially * usefull. */ for (inpb = tcbinfo.listhead->lh_first; inpb; inpb = inpb->inp_list.le_next) { if ((tcpb = intotcpcb(inpb))) { while ((te = LIST_FIRST(&tcpb->t_segq)) != NULL) { LIST_REMOVE(te, tqe_q); m_freem(te->tqe_m); FREE(te, M_TSEGQ); } } } } } /* * Notify a tcp user of an asynchronous error; * store error as soft error, but wake up user * (for now, won't do anything until can select for soft error). */ static void tcp_notify(inp, error) struct inpcb *inp; int error; { register struct tcpcb *tp = (struct tcpcb *)inp->inp_ppcb; register struct socket *so = inp->inp_socket; /* * Ignore some errors if we are hooked up. * If connection hasn't completed, has retransmitted several times, * and receives a second error, give up now. This is better * than waiting a long time to establish a connection that * can never complete. */ if (tp->t_state == TCPS_ESTABLISHED && (error == EHOSTUNREACH || error == ENETUNREACH || error == EHOSTDOWN)) { return; } else if (tp->t_state < TCPS_ESTABLISHED && tp->t_rxtshift > 3 && tp->t_softerror) so->so_error = error; else tp->t_softerror = error; wakeup((caddr_t) &so->so_timeo); sorwakeup(so); sowwakeup(so); } static int tcp_pcblist(SYSCTL_HANDLER_ARGS) { int error, i, n, s; struct inpcb *inp, **inp_list; inp_gen_t gencnt; struct xinpgen xig; /* * The process of preparing the TCB list is too time-consuming and * resource-intensive to repeat twice on every request. */ if (req->oldptr == 0) { n = tcbinfo.ipi_count; req->oldidx = 2 * (sizeof xig) + (n + n/8) * sizeof(struct xtcpcb); return 0; } if (req->newptr != 0) return EPERM; /* * OK, now we're committed to doing something. */ s = splnet(); gencnt = tcbinfo.ipi_gencnt; n = tcbinfo.ipi_count; splx(s); xig.xig_len = sizeof xig; xig.xig_count = n; xig.xig_gen = gencnt; xig.xig_sogen = so_gencnt; error = SYSCTL_OUT(req, &xig, sizeof xig); if (error) return error; inp_list = malloc(n * sizeof *inp_list, M_TEMP, M_WAITOK); if (inp_list == 0) return ENOMEM; s = splnet(); for (inp = tcbinfo.listhead->lh_first, i = 0; inp && i < n; inp = inp->inp_list.le_next) { if (inp->inp_gencnt <= gencnt && !prison_xinpcb(req->p, inp)) inp_list[i++] = inp; } splx(s); n = i; error = 0; for (i = 0; i < n; i++) { inp = inp_list[i]; if (inp->inp_gencnt <= gencnt) { struct xtcpcb xt; caddr_t inp_ppcb; xt.xt_len = sizeof xt; /* XXX should avoid extra copy */ bcopy(inp, &xt.xt_inp, sizeof *inp); inp_ppcb = inp->inp_ppcb; if (inp_ppcb != NULL) bcopy(inp_ppcb, &xt.xt_tp, sizeof xt.xt_tp); else bzero((char *) &xt.xt_tp, sizeof xt.xt_tp); if (inp->inp_socket) sotoxsocket(inp->inp_socket, &xt.xt_socket); error = SYSCTL_OUT(req, &xt, sizeof xt); } } if (!error) { /* * Give the user an updated idea of our state. * If the generation differs from what we told * her before, she knows that something happened * while we were processing this request, and it * might be necessary to retry. */ s = splnet(); xig.xig_gen = tcbinfo.ipi_gencnt; xig.xig_sogen = so_gencnt; xig.xig_count = tcbinfo.ipi_count; splx(s); error = SYSCTL_OUT(req, &xig, sizeof xig); } free(inp_list, M_TEMP); return error; } SYSCTL_PROC(_net_inet_tcp, TCPCTL_PCBLIST, pcblist, CTLFLAG_RD, 0, 0, tcp_pcblist, "S,xtcpcb", "List of active TCP connections"); static int tcp_getcred(SYSCTL_HANDLER_ARGS) { struct sockaddr_in addrs[2]; struct inpcb *inp; int error, s; error = suser(req->p); if (error) return (error); error = SYSCTL_IN(req, addrs, sizeof(addrs)); if (error) return (error); s = splnet(); inp = in_pcblookup_hash(&tcbinfo, addrs[1].sin_addr, addrs[1].sin_port, addrs[0].sin_addr, addrs[0].sin_port, 0, NULL); if (inp == NULL || inp->inp_socket == NULL) { error = ENOENT; goto out; } error = SYSCTL_OUT(req, inp->inp_socket->so_cred, sizeof(struct ucred)); out: splx(s); return (error); } SYSCTL_PROC(_net_inet_tcp, OID_AUTO, getcred, CTLTYPE_OPAQUE|CTLFLAG_RW, 0, 0, tcp_getcred, "S,ucred", "Get the ucred of a TCP connection"); #ifdef INET6 static int tcp6_getcred(SYSCTL_HANDLER_ARGS) { struct sockaddr_in6 addrs[2]; struct inpcb *inp; int error, s, mapped = 0; error = suser(req->p); if (error) return (error); error = SYSCTL_IN(req, addrs, sizeof(addrs)); if (error) return (error); if (IN6_IS_ADDR_V4MAPPED(&addrs[0].sin6_addr)) { if (IN6_IS_ADDR_V4MAPPED(&addrs[1].sin6_addr)) mapped = 1; else return (EINVAL); } s = splnet(); if (mapped == 1) inp = in_pcblookup_hash(&tcbinfo, *(struct in_addr *)&addrs[1].sin6_addr.s6_addr[12], addrs[1].sin6_port, *(struct in_addr *)&addrs[0].sin6_addr.s6_addr[12], addrs[0].sin6_port, 0, NULL); else inp = in6_pcblookup_hash(&tcbinfo, &addrs[1].sin6_addr, addrs[1].sin6_port, &addrs[0].sin6_addr, addrs[0].sin6_port, 0, NULL); if (inp == NULL || inp->inp_socket == NULL) { error = ENOENT; goto out; } error = SYSCTL_OUT(req, inp->inp_socket->so_cred, sizeof(struct ucred)); out: splx(s); return (error); } SYSCTL_PROC(_net_inet6_tcp6, OID_AUTO, getcred, CTLTYPE_OPAQUE|CTLFLAG_RW, 0, 0, tcp6_getcred, "S,ucred", "Get the ucred of a TCP6 connection"); #endif void tcp_ctlinput(cmd, sa, vip) int cmd; struct sockaddr *sa; void *vip; { register struct ip *ip = vip; register struct tcphdr *th; void (*notify) __P((struct inpcb *, int)) = tcp_notify; if (cmd == PRC_QUENCH) notify = tcp_quench; else if (cmd == PRC_MSGSIZE) notify = tcp_mtudisc; else if (!PRC_IS_REDIRECT(cmd) && ((unsigned)cmd > PRC_NCMDS || inetctlerrmap[cmd] == 0)) return; if (ip) { th = (struct tcphdr *)((caddr_t)ip + (IP_VHL_HL(ip->ip_vhl) << 2)); in_pcbnotify(&tcb, sa, th->th_dport, ip->ip_src, th->th_sport, cmd, notify); } else in_pcbnotify(&tcb, sa, 0, zeroin_addr, 0, cmd, notify); } #ifdef INET6 void tcp6_ctlinput(cmd, sa, d) int cmd; struct sockaddr *sa; void *d; { register struct tcphdr *thp; struct tcphdr th; void (*notify) __P((struct inpcb *, int)) = tcp_notify; struct sockaddr_in6 sa6; struct ip6_hdr *ip6; struct mbuf *m; int off; if (sa->sa_family != AF_INET6 || sa->sa_len != sizeof(struct sockaddr_in6)) return; if (cmd == PRC_QUENCH) notify = tcp_quench; else if (cmd == PRC_MSGSIZE) notify = tcp_mtudisc; else if (!PRC_IS_REDIRECT(cmd) && ((unsigned)cmd > PRC_NCMDS || inet6ctlerrmap[cmd] == 0)) return; /* if the parameter is from icmp6, decode it. */ if (d != NULL) { struct ip6ctlparam *ip6cp = (struct ip6ctlparam *)d; m = ip6cp->ip6c_m; ip6 = ip6cp->ip6c_ip6; off = ip6cp->ip6c_off; } else { m = NULL; ip6 = NULL; } /* * Translate addresses into internal form. * Sa check if it is AF_INET6 is done at the top of this funciton. */ sa6 = *(struct sockaddr_in6 *)sa; if (IN6_IS_ADDR_LINKLOCAL(&sa6.sin6_addr) != 0 && m != NULL && m->m_pkthdr.rcvif != NULL) sa6.sin6_addr.s6_addr16[1] = htons(m->m_pkthdr.rcvif->if_index); if (ip6) { /* * XXX: We assume that when IPV6 is non NULL, * M and OFF are valid. */ struct in6_addr s; /* translate addresses into internal form */ memcpy(&s, &ip6->ip6_src, sizeof(s)); if (IN6_IS_ADDR_LINKLOCAL(&s) != 0 && m != NULL && m->m_pkthdr.rcvif != NULL) s.s6_addr16[1] = htons(m->m_pkthdr.rcvif->if_index); if (m->m_len < off + sizeof(*thp)) { /* * this should be rare case * because now MINCLSIZE is "(MHLEN + 1)", * so we compromise on this copy... */ m_copydata(m, off, sizeof(th), (caddr_t)&th); thp = &th; } else thp = (struct tcphdr *)(mtod(m, caddr_t) + off); in6_pcbnotify(&tcb, (struct sockaddr *)&sa6, thp->th_dport, &s, thp->th_sport, cmd, notify); } else in6_pcbnotify(&tcb, (struct sockaddr *)&sa6, 0, &zeroin6_addr, 0, cmd, notify); } #endif /* INET6 */ /* * When a source quench is received, close congestion window * to one segment. We will gradually open it again as we proceed. */ void tcp_quench(inp, errno) struct inpcb *inp; int errno; { struct tcpcb *tp = intotcpcb(inp); if (tp) tp->snd_cwnd = tp->t_maxseg; } /* * When `need fragmentation' ICMP is received, update our idea of the MSS * based on the new value in the route. Also nudge TCP to send something, * since we know the packet we just sent was dropped. * This duplicates some code in the tcp_mss() function in tcp_input.c. */ void tcp_mtudisc(inp, errno) struct inpcb *inp; int errno; { struct tcpcb *tp = intotcpcb(inp); struct rtentry *rt; struct rmxp_tao *taop; struct socket *so = inp->inp_socket; int offered; int mss; #ifdef INET6 int isipv6 = (tp->t_inpcb->inp_vflag & INP_IPV6) != 0; #endif /* INET6 */ if (tp) { #ifdef INET6 if (isipv6) rt = tcp_rtlookup6(inp); else #endif /* INET6 */ rt = tcp_rtlookup(inp); if (!rt || !rt->rt_rmx.rmx_mtu) { tp->t_maxopd = tp->t_maxseg = #ifdef INET6 isipv6 ? tcp_v6mssdflt : #endif /* INET6 */ tcp_mssdflt; return; } taop = rmx_taop(rt->rt_rmx); offered = taop->tao_mssopt; mss = rt->rt_rmx.rmx_mtu - #ifdef INET6 (isipv6 ? sizeof(struct ip6_hdr) + sizeof(struct tcphdr) : #endif /* INET6 */ sizeof(struct tcpiphdr) #ifdef INET6 ) #endif /* INET6 */ ; if (offered) mss = min(mss, offered); /* * XXX - The above conditional probably violates the TCP * spec. The problem is that, since we don't know the * other end's MSS, we are supposed to use a conservative * default. But, if we do that, then MTU discovery will * never actually take place, because the conservative * default is much less than the MTUs typically seen * on the Internet today. For the moment, we'll sweep * this under the carpet. * * The conservative default might not actually be a problem * if the only case this occurs is when sending an initial * SYN with options and data to a host we've never talked * to before. Then, they will reply with an MSS value which * will get recorded and the new parameters should get * recomputed. For Further Study. */ if (tp->t_maxopd <= mss) return; tp->t_maxopd = mss; if ((tp->t_flags & (TF_REQ_TSTMP|TF_NOOPT)) == TF_REQ_TSTMP && (tp->t_flags & TF_RCVD_TSTMP) == TF_RCVD_TSTMP) mss -= TCPOLEN_TSTAMP_APPA; if ((tp->t_flags & (TF_REQ_CC|TF_NOOPT)) == TF_REQ_CC && (tp->t_flags & TF_RCVD_CC) == TF_RCVD_CC) mss -= TCPOLEN_CC_APPA; #if (MCLBYTES & (MCLBYTES - 1)) == 0 if (mss > MCLBYTES) mss &= ~(MCLBYTES-1); #else if (mss > MCLBYTES) mss = mss / MCLBYTES * MCLBYTES; #endif if (so->so_snd.sb_hiwat < mss) mss = so->so_snd.sb_hiwat; tp->t_maxseg = mss; tcpstat.tcps_mturesent++; tp->t_rtttime = 0; tp->snd_nxt = tp->snd_una; tcp_output(tp); } } /* * Look-up the routing entry to the peer of this inpcb. If no route * is found and it cannot be allocated the return NULL. This routine * is called by TCP routines that access the rmx structure and by tcp_mss * to get the interface MTU. */ struct rtentry * tcp_rtlookup(inp) struct inpcb *inp; { struct route *ro; struct rtentry *rt; ro = &inp->inp_route; rt = ro->ro_rt; if (rt == NULL || !(rt->rt_flags & RTF_UP)) { /* No route yet, so try to acquire one */ if (inp->inp_faddr.s_addr != INADDR_ANY) { ro->ro_dst.sa_family = AF_INET; ro->ro_dst.sa_len = sizeof(ro->ro_dst); ((struct sockaddr_in *) &ro->ro_dst)->sin_addr = inp->inp_faddr; rtalloc(ro); rt = ro->ro_rt; } } return rt; } #ifdef INET6 struct rtentry * tcp_rtlookup6(inp) struct inpcb *inp; { struct route_in6 *ro6; struct rtentry *rt; ro6 = &inp->in6p_route; rt = ro6->ro_rt; if (rt == NULL || !(rt->rt_flags & RTF_UP)) { /* No route yet, so try to acquire one */ if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) { ro6->ro_dst.sin6_family = AF_INET6; ro6->ro_dst.sin6_len = sizeof(ro6->ro_dst); ro6->ro_dst.sin6_addr = inp->in6p_faddr; rtalloc((struct route *)ro6); rt = ro6->ro_rt; } } return rt; } #endif /* INET6 */ #ifdef IPSEC /* compute ESP/AH header size for TCP, including outer IP header. */ size_t ipsec_hdrsiz_tcp(tp) struct tcpcb *tp; { struct inpcb *inp; struct mbuf *m; size_t hdrsiz; struct ip *ip; #ifdef INET6 struct ip6_hdr *ip6; #endif /* INET6 */ struct tcphdr *th; if (!tp || !tp->t_template || !(inp = tp->t_inpcb)) return 0; MGETHDR(m, M_DONTWAIT, MT_DATA); if (!m) return 0; #ifdef INET6 if ((inp->inp_vflag & INP_IPV6) != 0) { ip6 = mtod(m, struct ip6_hdr *); th = (struct tcphdr *)(ip6 + 1); m->m_pkthdr.len = m->m_len = sizeof(struct ip6_hdr) + sizeof(struct tcphdr); bcopy((caddr_t)tp->t_template->tt_ipgen, (caddr_t)ip6, sizeof(struct ip6_hdr)); bcopy((caddr_t)&tp->t_template->tt_t, (caddr_t)th, sizeof(struct tcphdr)); hdrsiz = ipsec6_hdrsiz(m, IPSEC_DIR_OUTBOUND, inp); } else #endif /* INET6 */ { ip = mtod(m, struct ip *); th = (struct tcphdr *)(ip + 1); m->m_pkthdr.len = m->m_len = sizeof(struct tcpiphdr); bcopy((caddr_t)tp->t_template->tt_ipgen, (caddr_t)ip, sizeof(struct ip)); bcopy((caddr_t)&tp->t_template->tt_t, (caddr_t)th, sizeof(struct tcphdr)); hdrsiz = ipsec4_hdrsiz(m, IPSEC_DIR_OUTBOUND, inp); } m_free(m); return hdrsiz; } #endif /*IPSEC*/ /* * Return a pointer to the cached information about the remote host. * The cached information is stored in the protocol specific part of * the route metrics. */ struct rmxp_tao * tcp_gettaocache(inp) struct inpcb *inp; { struct rtentry *rt; #ifdef INET6 if ((inp->inp_vflag & INP_IPV6) != 0) rt = tcp_rtlookup6(inp); else #endif /* INET6 */ rt = tcp_rtlookup(inp); /* Make sure this is a host route and is up. */ if (rt == NULL || (rt->rt_flags & (RTF_UP|RTF_HOST)) != (RTF_UP|RTF_HOST)) return NULL; return rmx_taop(rt->rt_rmx); } /* * Clear all the TAO cache entries, called from tcp_init. * * XXX * This routine is just an empty one, because we assume that the routing * routing tables are initialized at the same time when TCP, so there is * nothing in the cache left over. */ static void tcp_cleartaocache() { } Index: head/sys/netinet/tcp_timewait.c =================================================================== --- head/sys/netinet/tcp_timewait.c (revision 62586) +++ head/sys/netinet/tcp_timewait.c (revision 62587) @@ -1,1301 +1,1297 @@ /* * Copyright (c) 1982, 1986, 1988, 1990, 1993, 1995 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)tcp_subr.c 8.2 (Berkeley) 5/24/95 * $FreeBSD$ */ #include "opt_compat.h" #include "opt_inet6.h" #include "opt_ipsec.h" #include "opt_tcpdebug.h" #include #include #include #include #include #include #include #include #ifdef INET6 #include #endif #include #include #include #include #include #include #include #define _IP_VHL #include #include #include #ifdef INET6 #include #endif #include #ifdef INET6 #include #endif #include #include #ifdef INET6 #include #endif #include #include #include #include #include #ifdef INET6 #include #endif #include #ifdef TCPDEBUG #include #endif #include #ifdef IPSEC #include +#ifdef INET6 +#include +#endif #endif /*IPSEC*/ #include int tcp_mssdflt = TCP_MSS; SYSCTL_INT(_net_inet_tcp, TCPCTL_MSSDFLT, mssdflt, CTLFLAG_RW, &tcp_mssdflt , 0, "Default TCP Maximum Segment Size"); #ifdef INET6 int tcp_v6mssdflt = TCP6_MSS; SYSCTL_INT(_net_inet_tcp, TCPCTL_V6MSSDFLT, v6mssdflt, CTLFLAG_RW, &tcp_v6mssdflt , 0, "Default TCP Maximum Segment Size for IPv6"); #endif #if 0 static int tcp_rttdflt = TCPTV_SRTTDFLT / PR_SLOWHZ; SYSCTL_INT(_net_inet_tcp, TCPCTL_RTTDFLT, rttdflt, CTLFLAG_RW, &tcp_rttdflt , 0, "Default maximum TCP Round Trip Time"); #endif static int tcp_do_rfc1323 = 1; SYSCTL_INT(_net_inet_tcp, TCPCTL_DO_RFC1323, rfc1323, CTLFLAG_RW, &tcp_do_rfc1323 , 0, "Enable rfc1323 (high performance TCP) extensions"); static int tcp_do_rfc1644 = 0; SYSCTL_INT(_net_inet_tcp, TCPCTL_DO_RFC1644, rfc1644, CTLFLAG_RW, &tcp_do_rfc1644 , 0, "Enable rfc1644 (TTCP) extensions"); static int tcp_tcbhashsize = 0; SYSCTL_INT(_net_inet_tcp, OID_AUTO, tcbhashsize, CTLFLAG_RD, &tcp_tcbhashsize, 0, "Size of TCP control-block hashtable"); static int do_tcpdrain = 1; SYSCTL_INT(_debug, OID_AUTO, do_tcpdrain, CTLFLAG_RW, &do_tcpdrain, 0, "Enable non Net3 compliant tcp_drain"); SYSCTL_INT(_net_inet_tcp, OID_AUTO, pcbcount, CTLFLAG_RD, &tcbinfo.ipi_count, 0, "Number of active PCBs"); static void tcp_cleartaocache __P((void)); static void tcp_notify __P((struct inpcb *, int)); /* * Target size of TCP PCB hash tables. Must be a power of two. * * Note that this can be overridden by the kernel environment * variable net.inet.tcp.tcbhashsize */ #ifndef TCBHASHSIZE #define TCBHASHSIZE 512 #endif /* * This is the actual shape of what we allocate using the zone * allocator. Doing it this way allows us to protect both structures * using the same generation count, and also eliminates the overhead * of allocating tcpcbs separately. By hiding the structure here, * we avoid changing most of the rest of the code (although it needs * to be changed, eventually, for greater efficiency). */ #define ALIGNMENT 32 #define ALIGNM1 (ALIGNMENT - 1) struct inp_tp { union { struct inpcb inp; char align[(sizeof(struct inpcb) + ALIGNM1) & ~ALIGNM1]; } inp_tp_u; struct tcpcb tcb; struct callout inp_tp_rexmt, inp_tp_persist, inp_tp_keep, inp_tp_2msl; struct callout inp_tp_delack; }; #undef ALIGNMENT #undef ALIGNM1 /* * Tcp initialization */ void tcp_init() { int hashsize; tcp_iss = random(); /* wrong, but better than a constant */ tcp_ccgen = 1; tcp_cleartaocache(); tcp_delacktime = TCPTV_DELACK; tcp_keepinit = TCPTV_KEEP_INIT; tcp_keepidle = TCPTV_KEEP_IDLE; tcp_keepintvl = TCPTV_KEEPINTVL; tcp_maxpersistidle = TCPTV_KEEP_IDLE; tcp_msl = TCPTV_MSL; LIST_INIT(&tcb); tcbinfo.listhead = &tcb; TUNABLE_INT_FETCH("net.inet.tcp.tcbhashsize", TCBHASHSIZE, hashsize); if (!powerof2(hashsize)) { printf("WARNING: TCB hash size not a power of 2\n"); hashsize = 512; /* safe default */ } tcp_tcbhashsize = hashsize; tcbinfo.hashbase = hashinit(hashsize, M_PCB, &tcbinfo.hashmask); tcbinfo.porthashbase = hashinit(hashsize, M_PCB, &tcbinfo.porthashmask); tcbinfo.ipi_zone = zinit("tcpcb", sizeof(struct inp_tp), maxsockets, ZONE_INTERRUPT, 0); #ifdef INET6 #define TCP_MINPROTOHDR (sizeof(struct ip6_hdr) + sizeof(struct tcphdr)) #else /* INET6 */ #define TCP_MINPROTOHDR (sizeof(struct tcpiphdr)) #endif /* INET6 */ if (max_protohdr < TCP_MINPROTOHDR) max_protohdr = TCP_MINPROTOHDR; if (max_linkhdr + TCP_MINPROTOHDR > MHLEN) panic("tcp_init"); #undef TCP_MINPROTOHDR } /* * Create template to be used to send tcp packets on a connection. * Call after host entry created, allocates an mbuf and fills * in a skeletal tcp/ip header, minimizing the amount of work * necessary when the connection is used. */ struct tcptemp * tcp_template(tp) struct tcpcb *tp; { register struct inpcb *inp = tp->t_inpcb; register struct mbuf *m; register struct tcptemp *n; if ((n = tp->t_template) == 0) { m = m_get(M_DONTWAIT, MT_HEADER); if (m == NULL) return (0); m->m_len = sizeof (struct tcptemp); n = mtod(m, struct tcptemp *); } #ifdef INET6 if ((inp->inp_vflag & INP_IPV6) != 0) { register struct ip6_hdr *ip6; ip6 = (struct ip6_hdr *)n->tt_ipgen; ip6->ip6_flow = (ip6->ip6_flow & ~IPV6_FLOWINFO_MASK) | (inp->in6p_flowinfo & IPV6_FLOWINFO_MASK); ip6->ip6_vfc = (ip6->ip6_vfc & ~IPV6_VERSION_MASK) | (IPV6_VERSION & IPV6_VERSION_MASK); ip6->ip6_nxt = IPPROTO_TCP; ip6->ip6_plen = sizeof(struct tcphdr); ip6->ip6_src = inp->in6p_laddr; ip6->ip6_dst = inp->in6p_faddr; n->tt_t.th_sum = 0; } else #endif { struct ip *ip = (struct ip *)n->tt_ipgen; bzero(ip, sizeof(struct ip)); /* XXX overkill? */ ip->ip_vhl = IP_VHL_BORING; ip->ip_p = IPPROTO_TCP; ip->ip_src = inp->inp_laddr; ip->ip_dst = inp->inp_faddr; n->tt_t.th_sum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr, htons(sizeof(struct tcphdr) + IPPROTO_TCP)); } n->tt_t.th_sport = inp->inp_lport; n->tt_t.th_dport = inp->inp_fport; n->tt_t.th_seq = 0; n->tt_t.th_ack = 0; n->tt_t.th_x2 = 0; n->tt_t.th_off = 5; n->tt_t.th_flags = 0; n->tt_t.th_win = 0; n->tt_t.th_urp = 0; return (n); } /* * Send a single message to the TCP at address specified by * the given TCP/IP header. If m == 0, then we make a copy * of the tcpiphdr at ti and send directly to the addressed host. * This is used to force keep alive messages out using the TCP * template for a connection tp->t_template. If flags are given * then we send a message back to the TCP which originated the * segment ti, and discard the mbuf containing it and any other * attached mbufs. * * In any case the ack and sequence number of the transmitted * segment are as specified by the parameters. * * NOTE: If m != NULL, then ti must point to *inside* the mbuf. */ void tcp_respond(tp, ipgen, th, m, ack, seq, flags) struct tcpcb *tp; void *ipgen; register struct tcphdr *th; register struct mbuf *m; tcp_seq ack, seq; int flags; { register int tlen; int win = 0; struct route *ro = 0; struct route sro; struct ip *ip; struct tcphdr *nth; #ifdef INET6 struct route_in6 *ro6 = 0; struct route_in6 sro6; struct ip6_hdr *ip6; int isipv6; #endif /* INET6 */ int ipflags = 0; #ifdef INET6 isipv6 = IP_VHL_V(((struct ip *)ipgen)->ip_vhl) == 6; ip6 = ipgen; #endif /* INET6 */ ip = ipgen; if (tp) { if (!(flags & TH_RST)) { win = sbspace(&tp->t_inpcb->inp_socket->so_rcv); if (win > (long)TCP_MAXWIN << tp->rcv_scale) win = (long)TCP_MAXWIN << tp->rcv_scale; } #ifdef INET6 if (isipv6) ro6 = &tp->t_inpcb->in6p_route; else #endif /* INET6 */ ro = &tp->t_inpcb->inp_route; } else { #ifdef INET6 if (isipv6) { ro6 = &sro6; bzero(ro6, sizeof *ro6); } else #endif /* INET6 */ { ro = &sro; bzero(ro, sizeof *ro); } } if (m == 0) { m = m_gethdr(M_DONTWAIT, MT_HEADER); if (m == NULL) return; #ifdef TCP_COMPAT_42 tlen = 1; #else tlen = 0; #endif m->m_data += max_linkhdr; #ifdef INET6 if (isipv6) { bcopy((caddr_t)ip6, mtod(m, caddr_t), sizeof(struct ip6_hdr)); ip6 = mtod(m, struct ip6_hdr *); nth = (struct tcphdr *)(ip6 + 1); } else #endif /* INET6 */ { bcopy((caddr_t)ip, mtod(m, caddr_t), sizeof(struct ip)); ip = mtod(m, struct ip *); nth = (struct tcphdr *)(ip + 1); } bcopy((caddr_t)th, (caddr_t)nth, sizeof(struct tcphdr)); flags = TH_ACK; } else { m_freem(m->m_next); m->m_next = 0; m->m_data = (caddr_t)ipgen; /* m_len is set later */ tlen = 0; #define xchg(a,b,type) { type t; t=a; a=b; b=t; } #ifdef INET6 if (isipv6) { xchg(ip6->ip6_dst, ip6->ip6_src, struct in6_addr); nth = (struct tcphdr *)(ip6 + 1); } else #endif /* INET6 */ { xchg(ip->ip_dst.s_addr, ip->ip_src.s_addr, n_long); nth = (struct tcphdr *)(ip + 1); } if (th != nth) { /* * this is usually a case when an extension header * exists between the IPv6 header and the * TCP header. */ nth->th_sport = th->th_sport; nth->th_dport = th->th_dport; } xchg(nth->th_dport, nth->th_sport, n_short); #undef xchg } #ifdef INET6 if (isipv6) { ip6->ip6_plen = htons((u_short)(sizeof (struct tcphdr) + tlen)); tlen += sizeof (struct ip6_hdr) + sizeof (struct tcphdr); } else #endif { tlen += sizeof (struct tcpiphdr); ip->ip_len = tlen; ip->ip_ttl = ip_defttl; } m->m_len = tlen; m->m_pkthdr.len = tlen; m->m_pkthdr.rcvif = (struct ifnet *) 0; nth->th_seq = htonl(seq); nth->th_ack = htonl(ack); nth->th_x2 = 0; nth->th_off = sizeof (struct tcphdr) >> 2; nth->th_flags = flags; if (tp) nth->th_win = htons((u_short) (win >> tp->rcv_scale)); else nth->th_win = htons((u_short)win); nth->th_urp = 0; #ifdef INET6 if (isipv6) { nth->th_sum = 0; nth->th_sum = in6_cksum(m, IPPROTO_TCP, sizeof(struct ip6_hdr), tlen - sizeof(struct ip6_hdr)); ip6->ip6_hlim = in6_selecthlim(tp ? tp->t_inpcb : NULL, ro6 && ro6->ro_rt ? ro6->ro_rt->rt_ifp : NULL); } else #endif /* INET6 */ { nth->th_sum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr, htons((u_short)(tlen - sizeof(struct ip) + ip->ip_p))); m->m_pkthdr.csum_flags = CSUM_TCP; m->m_pkthdr.csum_data = offsetof(struct tcphdr, th_sum); } #ifdef TCPDEBUG if (tp == NULL || (tp->t_inpcb->inp_socket->so_options & SO_DEBUG)) tcp_trace(TA_OUTPUT, 0, tp, mtod(m, void *), th, 0); #endif #ifdef IPSEC - if (tp != NULL) { - m->m_pkthdr.rcvif = (struct ifnet *)tp->t_inpcb->inp_socket; - ipflags |= -#ifdef INET6 - isipv6 ? IPV6_SOCKINMRCVIF : -#endif - IP_SOCKINMRCVIF; - } + ipsec_setsocket(m, tp ? tp->t_inpcb->inp_socket : NULL); #endif #ifdef INET6 if (isipv6) { (void)ip6_output(m, NULL, ro6, ipflags, NULL, NULL); if (ro6 == &sro6 && ro6->ro_rt) { RTFREE(ro6->ro_rt); ro6->ro_rt = NULL; } } else #endif /* INET6 */ { (void) ip_output(m, NULL, ro, ipflags, NULL); if (ro == &sro && ro->ro_rt) { RTFREE(ro->ro_rt); ro->ro_rt = NULL; } } } /* * Create a new TCP control block, making an * empty reassembly queue and hooking it to the argument * protocol control block. The `inp' parameter must have * come from the zone allocator set up in tcp_init(). */ struct tcpcb * tcp_newtcpcb(inp) struct inpcb *inp; { struct inp_tp *it; register struct tcpcb *tp; #ifdef INET6 int isipv6 = (inp->inp_vflag & INP_IPV6) != 0; #endif /* INET6 */ it = (struct inp_tp *)inp; tp = &it->tcb; bzero((char *) tp, sizeof(struct tcpcb)); LIST_INIT(&tp->t_segq); tp->t_maxseg = tp->t_maxopd = #ifdef INET6 isipv6 ? tcp_v6mssdflt : #endif /* INET6 */ tcp_mssdflt; /* Set up our timeouts. */ callout_init(tp->tt_rexmt = &it->inp_tp_rexmt); callout_init(tp->tt_persist = &it->inp_tp_persist); callout_init(tp->tt_keep = &it->inp_tp_keep); callout_init(tp->tt_2msl = &it->inp_tp_2msl); callout_init(tp->tt_delack = &it->inp_tp_delack); if (tcp_do_rfc1323) tp->t_flags = (TF_REQ_SCALE|TF_REQ_TSTMP); if (tcp_do_rfc1644) tp->t_flags |= TF_REQ_CC; tp->t_inpcb = inp; /* XXX */ /* * Init srtt to TCPTV_SRTTBASE (0), so we can tell that we have no * rtt estimate. Set rttvar so that srtt + 4 * rttvar gives * reasonable initial retransmit time. */ tp->t_srtt = TCPTV_SRTTBASE; tp->t_rttvar = ((TCPTV_RTOBASE - TCPTV_SRTTBASE) << TCP_RTTVAR_SHIFT) / 4; tp->t_rttmin = TCPTV_MIN; tp->t_rxtcur = TCPTV_RTOBASE; tp->snd_cwnd = TCP_MAXWIN << TCP_MAX_WINSHIFT; tp->snd_ssthresh = TCP_MAXWIN << TCP_MAX_WINSHIFT; tp->t_rcvtime = ticks; /* * IPv4 TTL initialization is necessary for an IPv6 socket as well, * because the socket may be bound to an IPv6 wildcard address, * which may match an IPv4-mapped IPv6 address. */ inp->inp_ip_ttl = ip_defttl; inp->inp_ppcb = (caddr_t)tp; return (tp); /* XXX */ } /* * Drop a TCP connection, reporting * the specified error. If connection is synchronized, * then send a RST to peer. */ struct tcpcb * tcp_drop(tp, errno) register struct tcpcb *tp; int errno; { struct socket *so = tp->t_inpcb->inp_socket; if (TCPS_HAVERCVDSYN(tp->t_state)) { tp->t_state = TCPS_CLOSED; (void) tcp_output(tp); tcpstat.tcps_drops++; } else tcpstat.tcps_conndrops++; if (errno == ETIMEDOUT && tp->t_softerror) errno = tp->t_softerror; so->so_error = errno; return (tcp_close(tp)); } /* * Close a TCP control block: * discard all space held by the tcp * discard internet protocol block * wake up any sleepers */ struct tcpcb * tcp_close(tp) register struct tcpcb *tp; { register struct tseg_qent *q; struct inpcb *inp = tp->t_inpcb; struct socket *so = inp->inp_socket; #ifdef INET6 int isipv6 = (inp->inp_vflag & INP_IPV6) != 0; #endif /* INET6 */ register struct rtentry *rt; int dosavessthresh; /* * Make sure that all of our timers are stopped before we * delete the PCB. */ callout_stop(tp->tt_rexmt); callout_stop(tp->tt_persist); callout_stop(tp->tt_keep); callout_stop(tp->tt_2msl); callout_stop(tp->tt_delack); /* * If we got enough samples through the srtt filter, * save the rtt and rttvar in the routing entry. * 'Enough' is arbitrarily defined as the 16 samples. * 16 samples is enough for the srtt filter to converge * to within 5% of the correct value; fewer samples and * we could save a very bogus rtt. * * Don't update the default route's characteristics and don't * update anything that the user "locked". */ if (tp->t_rttupdated >= 16) { register u_long i = 0; #ifdef INET6 if (isipv6) { struct sockaddr_in6 *sin6; if ((rt = inp->in6p_route.ro_rt) == NULL) goto no_valid_rt; sin6 = (struct sockaddr_in6 *)rt_key(rt); if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) goto no_valid_rt; } else #endif /* INET6 */ if ((rt = inp->inp_route.ro_rt) == NULL || ((struct sockaddr_in *)rt_key(rt))->sin_addr.s_addr == INADDR_ANY) goto no_valid_rt; if ((rt->rt_rmx.rmx_locks & RTV_RTT) == 0) { i = tp->t_srtt * (RTM_RTTUNIT / (hz * TCP_RTT_SCALE)); if (rt->rt_rmx.rmx_rtt && i) /* * filter this update to half the old & half * the new values, converting scale. * See route.h and tcp_var.h for a * description of the scaling constants. */ rt->rt_rmx.rmx_rtt = (rt->rt_rmx.rmx_rtt + i) / 2; else rt->rt_rmx.rmx_rtt = i; tcpstat.tcps_cachedrtt++; } if ((rt->rt_rmx.rmx_locks & RTV_RTTVAR) == 0) { i = tp->t_rttvar * (RTM_RTTUNIT / (hz * TCP_RTTVAR_SCALE)); if (rt->rt_rmx.rmx_rttvar && i) rt->rt_rmx.rmx_rttvar = (rt->rt_rmx.rmx_rttvar + i) / 2; else rt->rt_rmx.rmx_rttvar = i; tcpstat.tcps_cachedrttvar++; } /* * The old comment here said: * update the pipelimit (ssthresh) if it has been updated * already or if a pipesize was specified & the threshhold * got below half the pipesize. I.e., wait for bad news * before we start updating, then update on both good * and bad news. * * But we want to save the ssthresh even if no pipesize is * specified explicitly in the route, because such * connections still have an implicit pipesize specified * by the global tcp_sendspace. In the absence of a reliable * way to calculate the pipesize, it will have to do. */ i = tp->snd_ssthresh; if (rt->rt_rmx.rmx_sendpipe != 0) dosavessthresh = (i < rt->rt_rmx.rmx_sendpipe / 2); else dosavessthresh = (i < so->so_snd.sb_hiwat / 2); if (((rt->rt_rmx.rmx_locks & RTV_SSTHRESH) == 0 && i != 0 && rt->rt_rmx.rmx_ssthresh != 0) || dosavessthresh) { /* * convert the limit from user data bytes to * packets then to packet data bytes. */ i = (i + tp->t_maxseg / 2) / tp->t_maxseg; if (i < 2) i = 2; i *= (u_long)(tp->t_maxseg + #ifdef INET6 (isipv6 ? sizeof (struct ip6_hdr) + sizeof (struct tcphdr) : #endif sizeof (struct tcpiphdr) #ifdef INET6 ) #endif ); if (rt->rt_rmx.rmx_ssthresh) rt->rt_rmx.rmx_ssthresh = (rt->rt_rmx.rmx_ssthresh + i) / 2; else rt->rt_rmx.rmx_ssthresh = i; tcpstat.tcps_cachedssthresh++; } } no_valid_rt: /* free the reassembly queue, if any */ while((q = LIST_FIRST(&tp->t_segq)) != NULL) { LIST_REMOVE(q, tqe_q); m_freem(q->tqe_m); FREE(q, M_TSEGQ); } if (tp->t_template) (void) m_free(dtom(tp->t_template)); inp->inp_ppcb = NULL; soisdisconnected(so); #ifdef INET6 if (INP_CHECK_SOCKAF(so, AF_INET6)) in6_pcbdetach(inp); else #endif /* INET6 */ in_pcbdetach(inp); tcpstat.tcps_closed++; return ((struct tcpcb *)0); } void tcp_drain() { if (do_tcpdrain) { struct inpcb *inpb; struct tcpcb *tcpb; struct tseg_qent *te; /* * Walk the tcpbs, if existing, and flush the reassembly queue, * if there is one... * XXX: The "Net/3" implementation doesn't imply that the TCP * reassembly queue should be flushed, but in a situation * where we're really low on mbufs, this is potentially * usefull. */ for (inpb = tcbinfo.listhead->lh_first; inpb; inpb = inpb->inp_list.le_next) { if ((tcpb = intotcpcb(inpb))) { while ((te = LIST_FIRST(&tcpb->t_segq)) != NULL) { LIST_REMOVE(te, tqe_q); m_freem(te->tqe_m); FREE(te, M_TSEGQ); } } } } } /* * Notify a tcp user of an asynchronous error; * store error as soft error, but wake up user * (for now, won't do anything until can select for soft error). */ static void tcp_notify(inp, error) struct inpcb *inp; int error; { register struct tcpcb *tp = (struct tcpcb *)inp->inp_ppcb; register struct socket *so = inp->inp_socket; /* * Ignore some errors if we are hooked up. * If connection hasn't completed, has retransmitted several times, * and receives a second error, give up now. This is better * than waiting a long time to establish a connection that * can never complete. */ if (tp->t_state == TCPS_ESTABLISHED && (error == EHOSTUNREACH || error == ENETUNREACH || error == EHOSTDOWN)) { return; } else if (tp->t_state < TCPS_ESTABLISHED && tp->t_rxtshift > 3 && tp->t_softerror) so->so_error = error; else tp->t_softerror = error; wakeup((caddr_t) &so->so_timeo); sorwakeup(so); sowwakeup(so); } static int tcp_pcblist(SYSCTL_HANDLER_ARGS) { int error, i, n, s; struct inpcb *inp, **inp_list; inp_gen_t gencnt; struct xinpgen xig; /* * The process of preparing the TCB list is too time-consuming and * resource-intensive to repeat twice on every request. */ if (req->oldptr == 0) { n = tcbinfo.ipi_count; req->oldidx = 2 * (sizeof xig) + (n + n/8) * sizeof(struct xtcpcb); return 0; } if (req->newptr != 0) return EPERM; /* * OK, now we're committed to doing something. */ s = splnet(); gencnt = tcbinfo.ipi_gencnt; n = tcbinfo.ipi_count; splx(s); xig.xig_len = sizeof xig; xig.xig_count = n; xig.xig_gen = gencnt; xig.xig_sogen = so_gencnt; error = SYSCTL_OUT(req, &xig, sizeof xig); if (error) return error; inp_list = malloc(n * sizeof *inp_list, M_TEMP, M_WAITOK); if (inp_list == 0) return ENOMEM; s = splnet(); for (inp = tcbinfo.listhead->lh_first, i = 0; inp && i < n; inp = inp->inp_list.le_next) { if (inp->inp_gencnt <= gencnt && !prison_xinpcb(req->p, inp)) inp_list[i++] = inp; } splx(s); n = i; error = 0; for (i = 0; i < n; i++) { inp = inp_list[i]; if (inp->inp_gencnt <= gencnt) { struct xtcpcb xt; caddr_t inp_ppcb; xt.xt_len = sizeof xt; /* XXX should avoid extra copy */ bcopy(inp, &xt.xt_inp, sizeof *inp); inp_ppcb = inp->inp_ppcb; if (inp_ppcb != NULL) bcopy(inp_ppcb, &xt.xt_tp, sizeof xt.xt_tp); else bzero((char *) &xt.xt_tp, sizeof xt.xt_tp); if (inp->inp_socket) sotoxsocket(inp->inp_socket, &xt.xt_socket); error = SYSCTL_OUT(req, &xt, sizeof xt); } } if (!error) { /* * Give the user an updated idea of our state. * If the generation differs from what we told * her before, she knows that something happened * while we were processing this request, and it * might be necessary to retry. */ s = splnet(); xig.xig_gen = tcbinfo.ipi_gencnt; xig.xig_sogen = so_gencnt; xig.xig_count = tcbinfo.ipi_count; splx(s); error = SYSCTL_OUT(req, &xig, sizeof xig); } free(inp_list, M_TEMP); return error; } SYSCTL_PROC(_net_inet_tcp, TCPCTL_PCBLIST, pcblist, CTLFLAG_RD, 0, 0, tcp_pcblist, "S,xtcpcb", "List of active TCP connections"); static int tcp_getcred(SYSCTL_HANDLER_ARGS) { struct sockaddr_in addrs[2]; struct inpcb *inp; int error, s; error = suser(req->p); if (error) return (error); error = SYSCTL_IN(req, addrs, sizeof(addrs)); if (error) return (error); s = splnet(); inp = in_pcblookup_hash(&tcbinfo, addrs[1].sin_addr, addrs[1].sin_port, addrs[0].sin_addr, addrs[0].sin_port, 0, NULL); if (inp == NULL || inp->inp_socket == NULL) { error = ENOENT; goto out; } error = SYSCTL_OUT(req, inp->inp_socket->so_cred, sizeof(struct ucred)); out: splx(s); return (error); } SYSCTL_PROC(_net_inet_tcp, OID_AUTO, getcred, CTLTYPE_OPAQUE|CTLFLAG_RW, 0, 0, tcp_getcred, "S,ucred", "Get the ucred of a TCP connection"); #ifdef INET6 static int tcp6_getcred(SYSCTL_HANDLER_ARGS) { struct sockaddr_in6 addrs[2]; struct inpcb *inp; int error, s, mapped = 0; error = suser(req->p); if (error) return (error); error = SYSCTL_IN(req, addrs, sizeof(addrs)); if (error) return (error); if (IN6_IS_ADDR_V4MAPPED(&addrs[0].sin6_addr)) { if (IN6_IS_ADDR_V4MAPPED(&addrs[1].sin6_addr)) mapped = 1; else return (EINVAL); } s = splnet(); if (mapped == 1) inp = in_pcblookup_hash(&tcbinfo, *(struct in_addr *)&addrs[1].sin6_addr.s6_addr[12], addrs[1].sin6_port, *(struct in_addr *)&addrs[0].sin6_addr.s6_addr[12], addrs[0].sin6_port, 0, NULL); else inp = in6_pcblookup_hash(&tcbinfo, &addrs[1].sin6_addr, addrs[1].sin6_port, &addrs[0].sin6_addr, addrs[0].sin6_port, 0, NULL); if (inp == NULL || inp->inp_socket == NULL) { error = ENOENT; goto out; } error = SYSCTL_OUT(req, inp->inp_socket->so_cred, sizeof(struct ucred)); out: splx(s); return (error); } SYSCTL_PROC(_net_inet6_tcp6, OID_AUTO, getcred, CTLTYPE_OPAQUE|CTLFLAG_RW, 0, 0, tcp6_getcred, "S,ucred", "Get the ucred of a TCP6 connection"); #endif void tcp_ctlinput(cmd, sa, vip) int cmd; struct sockaddr *sa; void *vip; { register struct ip *ip = vip; register struct tcphdr *th; void (*notify) __P((struct inpcb *, int)) = tcp_notify; if (cmd == PRC_QUENCH) notify = tcp_quench; else if (cmd == PRC_MSGSIZE) notify = tcp_mtudisc; else if (!PRC_IS_REDIRECT(cmd) && ((unsigned)cmd > PRC_NCMDS || inetctlerrmap[cmd] == 0)) return; if (ip) { th = (struct tcphdr *)((caddr_t)ip + (IP_VHL_HL(ip->ip_vhl) << 2)); in_pcbnotify(&tcb, sa, th->th_dport, ip->ip_src, th->th_sport, cmd, notify); } else in_pcbnotify(&tcb, sa, 0, zeroin_addr, 0, cmd, notify); } #ifdef INET6 void tcp6_ctlinput(cmd, sa, d) int cmd; struct sockaddr *sa; void *d; { register struct tcphdr *thp; struct tcphdr th; void (*notify) __P((struct inpcb *, int)) = tcp_notify; struct sockaddr_in6 sa6; struct ip6_hdr *ip6; struct mbuf *m; int off; if (sa->sa_family != AF_INET6 || sa->sa_len != sizeof(struct sockaddr_in6)) return; if (cmd == PRC_QUENCH) notify = tcp_quench; else if (cmd == PRC_MSGSIZE) notify = tcp_mtudisc; else if (!PRC_IS_REDIRECT(cmd) && ((unsigned)cmd > PRC_NCMDS || inet6ctlerrmap[cmd] == 0)) return; /* if the parameter is from icmp6, decode it. */ if (d != NULL) { struct ip6ctlparam *ip6cp = (struct ip6ctlparam *)d; m = ip6cp->ip6c_m; ip6 = ip6cp->ip6c_ip6; off = ip6cp->ip6c_off; } else { m = NULL; ip6 = NULL; } /* * Translate addresses into internal form. * Sa check if it is AF_INET6 is done at the top of this funciton. */ sa6 = *(struct sockaddr_in6 *)sa; if (IN6_IS_ADDR_LINKLOCAL(&sa6.sin6_addr) != 0 && m != NULL && m->m_pkthdr.rcvif != NULL) sa6.sin6_addr.s6_addr16[1] = htons(m->m_pkthdr.rcvif->if_index); if (ip6) { /* * XXX: We assume that when IPV6 is non NULL, * M and OFF are valid. */ struct in6_addr s; /* translate addresses into internal form */ memcpy(&s, &ip6->ip6_src, sizeof(s)); if (IN6_IS_ADDR_LINKLOCAL(&s) != 0 && m != NULL && m->m_pkthdr.rcvif != NULL) s.s6_addr16[1] = htons(m->m_pkthdr.rcvif->if_index); if (m->m_len < off + sizeof(*thp)) { /* * this should be rare case * because now MINCLSIZE is "(MHLEN + 1)", * so we compromise on this copy... */ m_copydata(m, off, sizeof(th), (caddr_t)&th); thp = &th; } else thp = (struct tcphdr *)(mtod(m, caddr_t) + off); in6_pcbnotify(&tcb, (struct sockaddr *)&sa6, thp->th_dport, &s, thp->th_sport, cmd, notify); } else in6_pcbnotify(&tcb, (struct sockaddr *)&sa6, 0, &zeroin6_addr, 0, cmd, notify); } #endif /* INET6 */ /* * When a source quench is received, close congestion window * to one segment. We will gradually open it again as we proceed. */ void tcp_quench(inp, errno) struct inpcb *inp; int errno; { struct tcpcb *tp = intotcpcb(inp); if (tp) tp->snd_cwnd = tp->t_maxseg; } /* * When `need fragmentation' ICMP is received, update our idea of the MSS * based on the new value in the route. Also nudge TCP to send something, * since we know the packet we just sent was dropped. * This duplicates some code in the tcp_mss() function in tcp_input.c. */ void tcp_mtudisc(inp, errno) struct inpcb *inp; int errno; { struct tcpcb *tp = intotcpcb(inp); struct rtentry *rt; struct rmxp_tao *taop; struct socket *so = inp->inp_socket; int offered; int mss; #ifdef INET6 int isipv6 = (tp->t_inpcb->inp_vflag & INP_IPV6) != 0; #endif /* INET6 */ if (tp) { #ifdef INET6 if (isipv6) rt = tcp_rtlookup6(inp); else #endif /* INET6 */ rt = tcp_rtlookup(inp); if (!rt || !rt->rt_rmx.rmx_mtu) { tp->t_maxopd = tp->t_maxseg = #ifdef INET6 isipv6 ? tcp_v6mssdflt : #endif /* INET6 */ tcp_mssdflt; return; } taop = rmx_taop(rt->rt_rmx); offered = taop->tao_mssopt; mss = rt->rt_rmx.rmx_mtu - #ifdef INET6 (isipv6 ? sizeof(struct ip6_hdr) + sizeof(struct tcphdr) : #endif /* INET6 */ sizeof(struct tcpiphdr) #ifdef INET6 ) #endif /* INET6 */ ; if (offered) mss = min(mss, offered); /* * XXX - The above conditional probably violates the TCP * spec. The problem is that, since we don't know the * other end's MSS, we are supposed to use a conservative * default. But, if we do that, then MTU discovery will * never actually take place, because the conservative * default is much less than the MTUs typically seen * on the Internet today. For the moment, we'll sweep * this under the carpet. * * The conservative default might not actually be a problem * if the only case this occurs is when sending an initial * SYN with options and data to a host we've never talked * to before. Then, they will reply with an MSS value which * will get recorded and the new parameters should get * recomputed. For Further Study. */ if (tp->t_maxopd <= mss) return; tp->t_maxopd = mss; if ((tp->t_flags & (TF_REQ_TSTMP|TF_NOOPT)) == TF_REQ_TSTMP && (tp->t_flags & TF_RCVD_TSTMP) == TF_RCVD_TSTMP) mss -= TCPOLEN_TSTAMP_APPA; if ((tp->t_flags & (TF_REQ_CC|TF_NOOPT)) == TF_REQ_CC && (tp->t_flags & TF_RCVD_CC) == TF_RCVD_CC) mss -= TCPOLEN_CC_APPA; #if (MCLBYTES & (MCLBYTES - 1)) == 0 if (mss > MCLBYTES) mss &= ~(MCLBYTES-1); #else if (mss > MCLBYTES) mss = mss / MCLBYTES * MCLBYTES; #endif if (so->so_snd.sb_hiwat < mss) mss = so->so_snd.sb_hiwat; tp->t_maxseg = mss; tcpstat.tcps_mturesent++; tp->t_rtttime = 0; tp->snd_nxt = tp->snd_una; tcp_output(tp); } } /* * Look-up the routing entry to the peer of this inpcb. If no route * is found and it cannot be allocated the return NULL. This routine * is called by TCP routines that access the rmx structure and by tcp_mss * to get the interface MTU. */ struct rtentry * tcp_rtlookup(inp) struct inpcb *inp; { struct route *ro; struct rtentry *rt; ro = &inp->inp_route; rt = ro->ro_rt; if (rt == NULL || !(rt->rt_flags & RTF_UP)) { /* No route yet, so try to acquire one */ if (inp->inp_faddr.s_addr != INADDR_ANY) { ro->ro_dst.sa_family = AF_INET; ro->ro_dst.sa_len = sizeof(ro->ro_dst); ((struct sockaddr_in *) &ro->ro_dst)->sin_addr = inp->inp_faddr; rtalloc(ro); rt = ro->ro_rt; } } return rt; } #ifdef INET6 struct rtentry * tcp_rtlookup6(inp) struct inpcb *inp; { struct route_in6 *ro6; struct rtentry *rt; ro6 = &inp->in6p_route; rt = ro6->ro_rt; if (rt == NULL || !(rt->rt_flags & RTF_UP)) { /* No route yet, so try to acquire one */ if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) { ro6->ro_dst.sin6_family = AF_INET6; ro6->ro_dst.sin6_len = sizeof(ro6->ro_dst); ro6->ro_dst.sin6_addr = inp->in6p_faddr; rtalloc((struct route *)ro6); rt = ro6->ro_rt; } } return rt; } #endif /* INET6 */ #ifdef IPSEC /* compute ESP/AH header size for TCP, including outer IP header. */ size_t ipsec_hdrsiz_tcp(tp) struct tcpcb *tp; { struct inpcb *inp; struct mbuf *m; size_t hdrsiz; struct ip *ip; #ifdef INET6 struct ip6_hdr *ip6; #endif /* INET6 */ struct tcphdr *th; if (!tp || !tp->t_template || !(inp = tp->t_inpcb)) return 0; MGETHDR(m, M_DONTWAIT, MT_DATA); if (!m) return 0; #ifdef INET6 if ((inp->inp_vflag & INP_IPV6) != 0) { ip6 = mtod(m, struct ip6_hdr *); th = (struct tcphdr *)(ip6 + 1); m->m_pkthdr.len = m->m_len = sizeof(struct ip6_hdr) + sizeof(struct tcphdr); bcopy((caddr_t)tp->t_template->tt_ipgen, (caddr_t)ip6, sizeof(struct ip6_hdr)); bcopy((caddr_t)&tp->t_template->tt_t, (caddr_t)th, sizeof(struct tcphdr)); hdrsiz = ipsec6_hdrsiz(m, IPSEC_DIR_OUTBOUND, inp); } else #endif /* INET6 */ { ip = mtod(m, struct ip *); th = (struct tcphdr *)(ip + 1); m->m_pkthdr.len = m->m_len = sizeof(struct tcpiphdr); bcopy((caddr_t)tp->t_template->tt_ipgen, (caddr_t)ip, sizeof(struct ip)); bcopy((caddr_t)&tp->t_template->tt_t, (caddr_t)th, sizeof(struct tcphdr)); hdrsiz = ipsec4_hdrsiz(m, IPSEC_DIR_OUTBOUND, inp); } m_free(m); return hdrsiz; } #endif /*IPSEC*/ /* * Return a pointer to the cached information about the remote host. * The cached information is stored in the protocol specific part of * the route metrics. */ struct rmxp_tao * tcp_gettaocache(inp) struct inpcb *inp; { struct rtentry *rt; #ifdef INET6 if ((inp->inp_vflag & INP_IPV6) != 0) rt = tcp_rtlookup6(inp); else #endif /* INET6 */ rt = tcp_rtlookup(inp); /* Make sure this is a host route and is up. */ if (rt == NULL || (rt->rt_flags & (RTF_UP|RTF_HOST)) != (RTF_UP|RTF_HOST)) return NULL; return rmx_taop(rt->rt_rmx); } /* * Clear all the TAO cache entries, called from tcp_init. * * XXX * This routine is just an empty one, because we assume that the routing * routing tables are initialized at the same time when TCP, so there is * nothing in the cache left over. */ static void tcp_cleartaocache() { } Index: head/sys/netinet/udp_usrreq.c =================================================================== --- head/sys/netinet/udp_usrreq.c (revision 62586) +++ head/sys/netinet/udp_usrreq.c (revision 62587) @@ -1,904 +1,906 @@ /* * Copyright (c) 1982, 1986, 1988, 1990, 1993, 1995 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)udp_usrreq.c 8.6 (Berkeley) 5/23/95 * $FreeBSD$ */ #include "opt_ipsec.h" #include "opt_inet6.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef INET6 #include #endif #include #include #include #ifdef INET6 #include #endif #include #include #include #include #ifdef IPSEC #include #endif /*IPSEC*/ #include /* * UDP protocol implementation. * Per RFC 768, August, 1980. */ #ifndef COMPAT_42 static int udpcksum = 1; #else static int udpcksum = 0; /* XXX */ #endif SYSCTL_INT(_net_inet_udp, UDPCTL_CHECKSUM, checksum, CTLFLAG_RW, &udpcksum, 0, ""); int log_in_vain = 0; SYSCTL_INT(_net_inet_udp, OID_AUTO, log_in_vain, CTLFLAG_RW, &log_in_vain, 0, "Log all incoming UDP packets"); static int blackhole = 0; SYSCTL_INT(_net_inet_udp, OID_AUTO, blackhole, CTLFLAG_RW, &blackhole, 0, "Do not send port unreachables for refused connects"); struct inpcbhead udb; /* from udp_var.h */ #define udb6 udb /* for KAME src sync over BSD*'s */ struct inpcbinfo udbinfo; #ifndef UDBHASHSIZE #define UDBHASHSIZE 16 #endif struct udpstat udpstat; /* from udp_var.h */ SYSCTL_STRUCT(_net_inet_udp, UDPCTL_STATS, stats, CTLFLAG_RD, &udpstat, udpstat, "UDP statistics (struct udpstat, netinet/udp_var.h)"); static struct sockaddr_in udp_in = { sizeof(udp_in), AF_INET }; #ifdef INET6 struct udp_in6 { struct sockaddr_in6 uin6_sin; u_char uin6_init_done : 1; } udp_in6 = { { sizeof(udp_in6.uin6_sin), AF_INET6 }, 0 }; struct udp_ip6 { struct ip6_hdr uip6_ip6; u_char uip6_init_done : 1; } udp_ip6; #endif /* INET6 */ static void udp_append __P((struct inpcb *last, struct ip *ip, struct mbuf *n, int off)); #ifdef INET6 static void ip_2_ip6_hdr __P((struct ip6_hdr *ip6, struct ip *ip)); #endif static int udp_detach __P((struct socket *so)); static int udp_output __P((struct inpcb *, struct mbuf *, struct sockaddr *, struct mbuf *, struct proc *)); void udp_init() { LIST_INIT(&udb); udbinfo.listhead = &udb; udbinfo.hashbase = hashinit(UDBHASHSIZE, M_PCB, &udbinfo.hashmask); udbinfo.porthashbase = hashinit(UDBHASHSIZE, M_PCB, &udbinfo.porthashmask); udbinfo.ipi_zone = zinit("udpcb", sizeof(struct inpcb), maxsockets, ZONE_INTERRUPT, 0); } void udp_input(m, off, proto) register struct mbuf *m; int off, proto; { int iphlen = off; register struct ip *ip; register struct udphdr *uh; register struct inpcb *inp; struct mbuf *opts = 0; int len; struct ip save_ip; struct sockaddr *append_sa; udpstat.udps_ipackets++; /* * Strip IP options, if any; should skip this, * make available to user, and use on returned packets, * but we don't yet have a way to check the checksum * with options still present. */ if (iphlen > sizeof (struct ip)) { ip_stripoptions(m, (struct mbuf *)0); iphlen = sizeof(struct ip); } /* * Get IP and UDP header together in first mbuf. */ ip = mtod(m, struct ip *); if (m->m_len < iphlen + sizeof(struct udphdr)) { if ((m = m_pullup(m, iphlen + sizeof(struct udphdr))) == 0) { udpstat.udps_hdrops++; return; } ip = mtod(m, struct ip *); } uh = (struct udphdr *)((caddr_t)ip + iphlen); + /* destination port of 0 is illegal, based on RFC768. */ + if (uh->uh_dport == 0) + goto bad; + /* * Make mbuf data length reflect UDP length. * If not enough data to reflect UDP length, drop. */ len = ntohs((u_short)uh->uh_ulen); if (ip->ip_len != len) { if (len > ip->ip_len || len < sizeof(struct udphdr)) { udpstat.udps_badlen++; goto bad; } m_adj(m, len - ip->ip_len); /* ip->ip_len = len; */ } /* * Save a copy of the IP header in case we want restore it * for sending an ICMP error message in response. */ save_ip = *ip; /* * Checksum extended UDP header and data. */ if (uh->uh_sum) { if (m->m_pkthdr.csum_flags & CSUM_DATA_VALID) { if (m->m_pkthdr.csum_flags & CSUM_PSEUDO_HDR) uh->uh_sum = m->m_pkthdr.csum_data; else uh->uh_sum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr, htonl(ip->ip_len + m->m_pkthdr.csum_data + IPPROTO_UDP)); uh->uh_sum ^= 0xffff; } else { bzero(((struct ipovly *)ip)->ih_x1, 9); ((struct ipovly *)ip)->ih_len = uh->uh_ulen; uh->uh_sum = in_cksum(m, len + sizeof (struct ip)); } if (uh->uh_sum) { udpstat.udps_badsum++; m_freem(m); return; } } if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr)) || in_broadcast(ip->ip_dst, m->m_pkthdr.rcvif)) { struct inpcb *last; /* * Deliver a multicast or broadcast datagram to *all* sockets * for which the local and remote addresses and ports match * those of the incoming datagram. This allows more than * one process to receive multi/broadcasts on the same port. * (This really ought to be done for unicast datagrams as * well, but that would cause problems with existing * applications that open both address-specific sockets and * a wildcard socket listening to the same port -- they would * end up receiving duplicates of every unicast datagram. * Those applications open the multiple sockets to overcome an * inadequacy of the UDP socket interface, but for backwards * compatibility we avoid the problem here rather than * fixing the interface. Maybe 4.5BSD will remedy this?) */ /* * Construct sockaddr format source address. */ udp_in.sin_port = uh->uh_sport; udp_in.sin_addr = ip->ip_src; /* * Locate pcb(s) for datagram. * (Algorithm copied from raw_intr().) */ last = NULL; #ifdef INET6 udp_in6.uin6_init_done = udp_ip6.uip6_init_done = 0; #endif LIST_FOREACH(inp, &udb, inp_list) { #ifdef INET6 if ((inp->inp_vflag & INP_IPV4) == 0) continue; #endif if (inp->inp_lport != uh->uh_dport) continue; if (inp->inp_laddr.s_addr != INADDR_ANY) { if (inp->inp_laddr.s_addr != ip->ip_dst.s_addr) continue; } if (inp->inp_faddr.s_addr != INADDR_ANY) { if (inp->inp_faddr.s_addr != ip->ip_src.s_addr || inp->inp_fport != uh->uh_sport) continue; } if (last != NULL) { struct mbuf *n; #ifdef IPSEC /* check AH/ESP integrity. */ if (ipsec4_in_reject_so(m, last->inp_socket)) ipsecstat.in_polvio++; /* do not inject data to pcb */ else #endif /*IPSEC*/ if ((n = m_copy(m, 0, M_COPYALL)) != NULL) udp_append(last, ip, n, iphlen + sizeof(struct udphdr)); } last = inp; /* * Don't look for additional matches if this one does * not have either the SO_REUSEPORT or SO_REUSEADDR * socket options set. This heuristic avoids searching * through all pcbs in the common case of a non-shared * port. It * assumes that an application will never * clear these options after setting them. */ if ((last->inp_socket->so_options&(SO_REUSEPORT|SO_REUSEADDR)) == 0) break; } if (last == NULL) { /* * No matching pcb found; discard datagram. * (No need to send an ICMP Port Unreachable * for a broadcast or multicast datgram.) */ udpstat.udps_noportbcast++; goto bad; } #ifdef IPSEC /* check AH/ESP integrity. */ if (ipsec4_in_reject_so(m, last->inp_socket)) { ipsecstat.in_polvio++; goto bad; } #endif /*IPSEC*/ udp_append(last, ip, m, iphlen + sizeof(struct udphdr)); return; } /* * Locate pcb for datagram. */ inp = in_pcblookup_hash(&udbinfo, ip->ip_src, uh->uh_sport, ip->ip_dst, uh->uh_dport, 1, m->m_pkthdr.rcvif); if (inp == NULL) { if (log_in_vain) { char buf[4*sizeof "123"]; strcpy(buf, inet_ntoa(ip->ip_dst)); log(LOG_INFO, "Connection attempt to UDP %s:%d from %s:%d\n", buf, ntohs(uh->uh_dport), inet_ntoa(ip->ip_src), ntohs(uh->uh_sport)); } udpstat.udps_noport++; if (m->m_flags & (M_BCAST | M_MCAST)) { udpstat.udps_noportbcast++; goto bad; } *ip = save_ip; if (badport_bandlim(0) < 0) goto bad; if (blackhole) goto bad; icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_PORT, 0, 0); return; } #ifdef IPSEC if (ipsec4_in_reject_so(m, inp->inp_socket)) { ipsecstat.in_polvio++; goto bad; } #endif /*IPSEC*/ /* * Construct sockaddr format source address. * Stuff source address and datagram in user buffer. */ udp_in.sin_port = uh->uh_sport; udp_in.sin_addr = ip->ip_src; if (inp->inp_flags & INP_CONTROLOPTS || inp->inp_socket->so_options & SO_TIMESTAMP) { #ifdef INET6 if (inp->inp_vflag & INP_IPV6) { int savedflags; ip_2_ip6_hdr(&udp_ip6.uip6_ip6, ip); savedflags = inp->inp_flags; inp->inp_flags &= ~INP_UNMAPPABLEOPTS; ip6_savecontrol(inp, &opts, &udp_ip6.uip6_ip6, m); inp->inp_flags = savedflags; } else #endif ip_savecontrol(inp, &opts, ip, m); } iphlen += sizeof(struct udphdr); m_adj(m, iphlen); #ifdef INET6 if (inp->inp_vflag & INP_IPV6) { in6_sin_2_v4mapsin6(&udp_in, &udp_in6.uin6_sin); append_sa = (struct sockaddr *)&udp_in6; } else #endif append_sa = (struct sockaddr *)&udp_in; if (sbappendaddr(&inp->inp_socket->so_rcv, append_sa, m, opts) == 0) { udpstat.udps_fullsock++; goto bad; } sorwakeup(inp->inp_socket); return; bad: m_freem(m); if (opts) m_freem(opts); return; } -#if defined(INET6) +#ifdef INET6 static void ip_2_ip6_hdr(ip6, ip) struct ip6_hdr *ip6; struct ip *ip; { bzero(ip6, sizeof(*ip6)); ip6->ip6_vfc = IPV6_VERSION; ip6->ip6_plen = ip->ip_len; ip6->ip6_nxt = ip->ip_p; ip6->ip6_hlim = ip->ip_ttl; ip6->ip6_src.s6_addr32[2] = ip6->ip6_dst.s6_addr32[2] = IPV6_ADDR_INT32_SMP; ip6->ip6_src.s6_addr32[3] = ip->ip_src.s_addr; ip6->ip6_dst.s6_addr32[3] = ip->ip_dst.s_addr; } #endif /* * subroutine of udp_input(), mainly for source code readability. * caller must properly init udp_ip6 and udp_in6 beforehand. */ static void udp_append(last, ip, n, off) struct inpcb *last; struct ip *ip; struct mbuf *n; int off; { struct sockaddr *append_sa; struct mbuf *opts = 0; if (last->inp_flags & INP_CONTROLOPTS || last->inp_socket->so_options & SO_TIMESTAMP) { #ifdef INET6 if (last->inp_vflag & INP_IPV6) { int savedflags; if (udp_ip6.uip6_init_done == 0) { ip_2_ip6_hdr(&udp_ip6.uip6_ip6, ip); udp_ip6.uip6_init_done = 1; } savedflags = last->inp_flags; last->inp_flags &= ~INP_UNMAPPABLEOPTS; ip6_savecontrol(last, &opts, &udp_ip6.uip6_ip6, n); last->inp_flags = savedflags; } else #endif ip_savecontrol(last, &opts, ip, n); } #ifdef INET6 if (last->inp_vflag & INP_IPV6) { if (udp_in6.uin6_init_done == 0) { in6_sin_2_v4mapsin6(&udp_in, &udp_in6.uin6_sin); udp_in6.uin6_init_done = 1; } append_sa = (struct sockaddr *)&udp_in6.uin6_sin; } else #endif append_sa = (struct sockaddr *)&udp_in; m_adj(n, off); if (sbappendaddr(&last->inp_socket->so_rcv, append_sa, n, opts) == 0) { m_freem(n); if (opts) m_freem(opts); udpstat.udps_fullsock++; } else sorwakeup(last->inp_socket); } /* * Notify a udp user of an asynchronous error; * just wake up so that he can collect error status. */ void udp_notify(inp, errno) register struct inpcb *inp; int errno; { inp->inp_socket->so_error = errno; sorwakeup(inp->inp_socket); sowwakeup(inp->inp_socket); } void udp_ctlinput(cmd, sa, vip) int cmd; struct sockaddr *sa; void *vip; { register struct ip *ip = vip; register struct udphdr *uh; if (!PRC_IS_REDIRECT(cmd) && ((unsigned)cmd >= PRC_NCMDS || inetctlerrmap[cmd] == 0)) return; if (ip) { uh = (struct udphdr *)((caddr_t)ip + (ip->ip_hl << 2)); in_pcbnotify(&udb, sa, uh->uh_dport, ip->ip_src, uh->uh_sport, cmd, udp_notify); } else in_pcbnotify(&udb, sa, 0, zeroin_addr, 0, cmd, udp_notify); } static int udp_pcblist(SYSCTL_HANDLER_ARGS) { int error, i, n, s; struct inpcb *inp, **inp_list; inp_gen_t gencnt; struct xinpgen xig; /* * The process of preparing the TCB list is too time-consuming and * resource-intensive to repeat twice on every request. */ if (req->oldptr == 0) { n = udbinfo.ipi_count; req->oldidx = 2 * (sizeof xig) + (n + n/8) * sizeof(struct xinpcb); return 0; } if (req->newptr != 0) return EPERM; /* * OK, now we're committed to doing something. */ s = splnet(); gencnt = udbinfo.ipi_gencnt; n = udbinfo.ipi_count; splx(s); xig.xig_len = sizeof xig; xig.xig_count = n; xig.xig_gen = gencnt; xig.xig_sogen = so_gencnt; error = SYSCTL_OUT(req, &xig, sizeof xig); if (error) return error; inp_list = malloc(n * sizeof *inp_list, M_TEMP, M_WAITOK); if (inp_list == 0) return ENOMEM; s = splnet(); for (inp = udbinfo.listhead->lh_first, i = 0; inp && i < n; inp = inp->inp_list.le_next) { if (inp->inp_gencnt <= gencnt && !prison_xinpcb(req->p, inp)) inp_list[i++] = inp; } splx(s); n = i; error = 0; for (i = 0; i < n; i++) { inp = inp_list[i]; if (inp->inp_gencnt <= gencnt) { struct xinpcb xi; xi.xi_len = sizeof xi; /* XXX should avoid extra copy */ bcopy(inp, &xi.xi_inp, sizeof *inp); if (inp->inp_socket) sotoxsocket(inp->inp_socket, &xi.xi_socket); error = SYSCTL_OUT(req, &xi, sizeof xi); } } if (!error) { /* * Give the user an updated idea of our state. * If the generation differs from what we told * her before, she knows that something happened * while we were processing this request, and it * might be necessary to retry. */ s = splnet(); xig.xig_gen = udbinfo.ipi_gencnt; xig.xig_sogen = so_gencnt; xig.xig_count = udbinfo.ipi_count; splx(s); error = SYSCTL_OUT(req, &xig, sizeof xig); } free(inp_list, M_TEMP); return error; } SYSCTL_PROC(_net_inet_udp, UDPCTL_PCBLIST, pcblist, CTLFLAG_RD, 0, 0, udp_pcblist, "S,xinpcb", "List of active UDP sockets"); static int udp_getcred(SYSCTL_HANDLER_ARGS) { struct sockaddr_in addrs[2]; struct inpcb *inp; int error, s; error = suser(req->p); if (error) return (error); error = SYSCTL_IN(req, addrs, sizeof(addrs)); if (error) return (error); s = splnet(); inp = in_pcblookup_hash(&udbinfo, addrs[1].sin_addr, addrs[1].sin_port, addrs[0].sin_addr, addrs[0].sin_port, 1, NULL); if (inp == NULL || inp->inp_socket == NULL) { error = ENOENT; goto out; } error = SYSCTL_OUT(req, inp->inp_socket->so_cred, sizeof(struct ucred)); out: splx(s); return (error); } SYSCTL_PROC(_net_inet_udp, OID_AUTO, getcred, CTLTYPE_OPAQUE|CTLFLAG_RW, 0, 0, udp_getcred, "S,ucred", "Get the ucred of a UDP connection"); static int udp_output(inp, m, addr, control, p) register struct inpcb *inp; struct mbuf *m; struct sockaddr *addr; struct mbuf *control; struct proc *p; { register struct udpiphdr *ui; register int len = m->m_pkthdr.len; struct in_addr laddr; struct sockaddr_in *sin; int s = 0, error = 0; if (control) m_freem(control); /* XXX */ if (len + sizeof(struct udpiphdr) > IP_MAXPACKET) { error = EMSGSIZE; goto release; } if (addr) { sin = (struct sockaddr_in *)addr; prison_remote_ip(p, 0, &sin->sin_addr.s_addr); laddr = inp->inp_laddr; if (inp->inp_faddr.s_addr != INADDR_ANY) { error = EISCONN; goto release; } /* * Must block input while temporarily connected. */ s = splnet(); error = in_pcbconnect(inp, addr, p); if (error) { splx(s); goto release; } } else { if (inp->inp_faddr.s_addr == INADDR_ANY) { error = ENOTCONN; goto release; } } /* * Calculate data length and get a mbuf * for UDP and IP headers. */ M_PREPEND(m, sizeof(struct udpiphdr), M_DONTWAIT); if (m == 0) { error = ENOBUFS; if (addr) splx(s); goto release; } /* * Fill in mbuf with extended UDP header * and addresses and length put into network format. */ ui = mtod(m, struct udpiphdr *); bzero(ui->ui_x1, sizeof(ui->ui_x1)); /* XXX still needed? */ ui->ui_pr = IPPROTO_UDP; ui->ui_src = inp->inp_laddr; ui->ui_dst = inp->inp_faddr; ui->ui_sport = inp->inp_lport; ui->ui_dport = inp->inp_fport; ui->ui_ulen = htons((u_short)len + sizeof(struct udphdr)); /* * Set up checksum and output datagram. */ if (udpcksum) { ui->ui_sum = in_pseudo(ui->ui_src.s_addr, ui->ui_dst.s_addr, htons((u_short)len + sizeof(struct udphdr) + IPPROTO_UDP)); m->m_pkthdr.csum_flags = CSUM_UDP; m->m_pkthdr.csum_data = offsetof(struct udphdr, uh_sum); } else { ui->ui_sum = 0; } ((struct ip *)ui)->ip_len = sizeof (struct udpiphdr) + len; ((struct ip *)ui)->ip_ttl = inp->inp_ip_ttl; /* XXX */ ((struct ip *)ui)->ip_tos = inp->inp_ip_tos; /* XXX */ udpstat.udps_opackets++; #ifdef IPSEC - m->m_pkthdr.rcvif = (struct ifnet *)inp->inp_socket; + ipsec_setsocket(m, inp->inp_socket); #endif /*IPSEC*/ - error = ip_output(m, inp->inp_options, &inp->inp_route, - (inp->inp_socket->so_options & (SO_DONTROUTE | SO_BROADCAST)) - | IP_SOCKINMRCVIF, + (inp->inp_socket->so_options & (SO_DONTROUTE | SO_BROADCAST)), inp->inp_moptions); if (addr) { in_pcbdisconnect(inp); inp->inp_laddr = laddr; /* XXX rehash? */ splx(s); } return (error); release: m_freem(m); return (error); } u_long udp_sendspace = 9216; /* really max datagram size */ /* 40 1K datagrams */ SYSCTL_INT(_net_inet_udp, UDPCTL_MAXDGRAM, maxdgram, CTLFLAG_RW, &udp_sendspace, 0, "Maximum outgoing UDP datagram size"); u_long udp_recvspace = 40 * (1024 + #ifdef INET6 sizeof(struct sockaddr_in6) #else sizeof(struct sockaddr_in) #endif ); SYSCTL_INT(_net_inet_udp, UDPCTL_RECVSPACE, recvspace, CTLFLAG_RW, &udp_recvspace, 0, "Maximum incoming UDP datagram size"); static int udp_abort(struct socket *so) { struct inpcb *inp; int s; inp = sotoinpcb(so); if (inp == 0) return EINVAL; /* ??? possible? panic instead? */ soisdisconnected(so); s = splnet(); in_pcbdetach(inp); splx(s); return 0; } static int udp_attach(struct socket *so, int proto, struct proc *p) { struct inpcb *inp; int s, error; inp = sotoinpcb(so); if (inp != 0) return EINVAL; error = soreserve(so, udp_sendspace, udp_recvspace); if (error) return error; s = splnet(); error = in_pcballoc(so, &udbinfo, p); splx(s); if (error) return error; inp = (struct inpcb *)so->so_pcb; inp->inp_vflag |= INP_IPV4; inp->inp_ip_ttl = ip_defttl; #ifdef IPSEC error = ipsec_init_policy(so, &inp->inp_sp); if (error != 0) { in_pcbdetach(inp); return error; } #endif /*IPSEC*/ return 0; } static int udp_bind(struct socket *so, struct sockaddr *nam, struct proc *p) { struct inpcb *inp; int s, error; inp = sotoinpcb(so); if (inp == 0) return EINVAL; s = splnet(); error = in_pcbbind(inp, nam, p); splx(s); return error; } static int udp_connect(struct socket *so, struct sockaddr *nam, struct proc *p) { struct inpcb *inp; int s, error; struct sockaddr_in *sin; inp = sotoinpcb(so); if (inp == 0) return EINVAL; if (inp->inp_faddr.s_addr != INADDR_ANY) return EISCONN; s = splnet(); sin = (struct sockaddr_in *)nam; prison_remote_ip(p, 0, &sin->sin_addr.s_addr); error = in_pcbconnect(inp, nam, p); splx(s); if (error == 0) soisconnected(so); return error; } static int udp_detach(struct socket *so) { struct inpcb *inp; int s; inp = sotoinpcb(so); if (inp == 0) return EINVAL; s = splnet(); in_pcbdetach(inp); splx(s); return 0; } static int udp_disconnect(struct socket *so) { struct inpcb *inp; int s; inp = sotoinpcb(so); if (inp == 0) return EINVAL; if (inp->inp_faddr.s_addr == INADDR_ANY) return ENOTCONN; s = splnet(); in_pcbdisconnect(inp); inp->inp_laddr.s_addr = INADDR_ANY; splx(s); so->so_state &= ~SS_ISCONNECTED; /* XXX */ return 0; } static int udp_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr, struct mbuf *control, struct proc *p) { struct inpcb *inp; inp = sotoinpcb(so); if (inp == 0) { m_freem(m); return EINVAL; } return udp_output(inp, m, addr, control, p); } int udp_shutdown(struct socket *so) { struct inpcb *inp; inp = sotoinpcb(so); if (inp == 0) return EINVAL; socantsendmore(so); return 0; } struct pr_usrreqs udp_usrreqs = { udp_abort, pru_accept_notsupp, udp_attach, udp_bind, udp_connect, pru_connect2_notsupp, in_control, udp_detach, udp_disconnect, pru_listen_notsupp, in_setpeeraddr, pru_rcvd_notsupp, pru_rcvoob_notsupp, udp_send, pru_sense_null, udp_shutdown, in_setsockaddr, sosend, soreceive, sopoll }; Index: head/sys/netinet6/README =================================================================== --- head/sys/netinet6/README (nonexistent) +++ head/sys/netinet6/README (revision 62587) @@ -0,0 +1,28 @@ +a note to committers about KAME tree +$FreeBSD$ +KAME project + + +FreeBSD IPv6/IPsec tree is from KAMEproject (http://www.kame.net/). +To synchronize KAME tree and FreeBSD better today and in the future, +please understand the following: + +- DO NOT MAKE COSTMETIC CHANGES. + "Cosmetic changes" here includes tabify, untabify, removal of space at EOL, + minor KNF items, and whatever adds more output lines on "diff freebsd kame". + To make future synchronization easier. it is critical to preserve certain + statements in the code. Also, as KAME tree supports all 4 BSDs (Free, Open, + Net, BSD/OS) in single shared tree, it is not always possible to backport + FreeBSD changes into KAME tree. So again, please do not make cosmetic + changes. Even if you think it a right thing, that will bite KAME guys badly + during upgrade attempts, and prevent us from synchronizing two trees. + (you don't usually make cosmetic changes against third-party code, do you?) + +- REPORT CHANGES/BUGS TO KAME GUYS. + It is not always possible for KAME guys to watch all the freebsd mailing + list traffic, as the traffic is HUGE. So if possible, please, inform + kame guys of changes you made in IPv6/IPsec related portion. Contact + path would be snap-users@kame.net or KAME PR database on www.kame.net. + (or to core@kame.net if it is necessary to make it confidential) + +Thank you for your cooperation and have a happy IPv6 life! Property changes on: head/sys/netinet6/README ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: head/sys/netinet6/ah.h =================================================================== --- head/sys/netinet6/ah.h (revision 62586) +++ head/sys/netinet6/ah.h (revision 62587) @@ -1,89 +1,90 @@ +/* $FreeBSD$ */ +/* $KAME: ah.h,v 1.10 2000/07/02 13:23:33 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* * RFC1826/2402 authentication header. */ #ifndef _NETINET6_AH_H_ -#define _NETINET6_AH_H_ +#define _NETINET6_AH_H_ struct secasvar; struct ah { u_int8_t ah_nxt; /* Next Header */ u_int8_t ah_len; /* Length of data, in 32bit */ u_int16_t ah_reserve; /* Reserved for future use */ u_int32_t ah_spi; /* Security parameter index */ /* variable size, 32bit bound*/ /* Authentication data */ }; struct newah { u_int8_t ah_nxt; /* Next Header */ u_int8_t ah_len; /* Length of data + 1, in 32bit */ u_int16_t ah_reserve; /* Reserved for future use */ u_int32_t ah_spi; /* Security parameter index */ u_int32_t ah_seq; /* Sequence number field */ /* variable size, 32bit bound*/ /* Authentication data */ }; struct ah_algorithm_state { struct secasvar *sav; void* foo; /*per algorithm data - maybe*/ }; struct ah_algorithm { int (*sumsiz) __P((struct secasvar *)); int (*mature) __P((struct secasvar *)); int keymin; /* in bits */ int keymax; /* in bits */ - void (*init) __P((struct ah_algorithm_state *, struct secasvar *)); - void (*update) __P((struct ah_algorithm_state *, const caddr_t, - size_t)); + const char *name; + int (*init) __P((struct ah_algorithm_state *, struct secasvar *)); + void (*update) __P((struct ah_algorithm_state *, caddr_t, size_t)); void (*result) __P((struct ah_algorithm_state *, caddr_t)); }; #define AH_MAXSUMSIZE 16 #ifdef _KERNEL extern struct ah_algorithm ah_algorithms[]; /* cksum routines */ extern int ah_hdrlen __P((struct secasvar *)); extern size_t ah_hdrsiz __P((struct ipsecrequest *)); -extern void ah4_input __P((struct mbuf *, int, int)); +extern void ah4_input __P((struct mbuf *, ...)); extern int ah4_output __P((struct mbuf *, struct ipsecrequest *)); -extern int ah4_calccksum __P((struct mbuf *, caddr_t, - struct ah_algorithm *, struct secasvar *)); -#endif +extern int ah4_calccksum __P((struct mbuf *, caddr_t, size_t, + struct ah_algorithm *, struct secasvar *)); +#endif /*_KERNEL*/ #endif /*_NETINET6_AH_H_*/ Index: head/sys/netinet6/ah6.h =================================================================== --- head/sys/netinet6/ah6.h (revision 62586) +++ head/sys/netinet6/ah6.h (revision 62587) @@ -1,49 +1,50 @@ +/* $FreeBSD$ */ +/* $KAME: ah.h,v 1.10 2000/07/02 13:23:33 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* * RFC1826/2402 authentication header. */ #ifndef _NETINET6_AH6_H_ -#define _NETINET6_AH6_H_ +#define _NETINET6_AH6_H_ #ifdef _KERNEL struct secasvar; extern int ah6_input __P((struct mbuf **, int *, int)); extern int ah6_output __P((struct mbuf *, u_char *, struct mbuf *, struct ipsecrequest *)); -extern int ah6_calccksum __P((struct mbuf *, caddr_t, - struct ah_algorithm *, struct secasvar *)); +extern int ah6_calccksum __P((struct mbuf *, caddr_t, size_t, + struct ah_algorithm *, struct secasvar *)); #endif #endif /*_NETINET6_AH6_H_*/ Index: head/sys/netinet6/ah_core.c =================================================================== --- head/sys/netinet6/ah_core.c (revision 62586) +++ head/sys/netinet6/ah_core.c (revision 62587) @@ -1,1125 +1,1143 @@ +/* $FreeBSD$ */ +/* $KAME: ah_core.c,v 1.35 2000/06/14 11:14:03 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* * RFC1826/2402 authentication header. */ +#include "opt_inet.h" #include "opt_inet6.h" #include "opt_ipsec.h" #include #include #include #include #include #include #include #include #include #include +#include #include #include #include #include #include #include -#include #ifdef INET6 -#include +#include #include -#include +#include #endif #include -#include #ifdef INET6 #include +#endif +#include +#ifdef INET6 #include #endif #ifdef IPSEC_ESP #include #ifdef INET6 #include #endif #endif #include -#include #include #include #include #include #define HMACSIZE 16 -#ifdef INET6 -#define ZEROBUFLEN 256 -static char zerobuf[ZEROBUFLEN]; -#endif - static int ah_sumsiz_1216 __P((struct secasvar *)); static int ah_sumsiz_zero __P((struct secasvar *)); static int ah_none_mature __P((struct secasvar *)); -static void ah_none_init __P((struct ah_algorithm_state *, +static int ah_none_init __P((struct ah_algorithm_state *, struct secasvar *)); -static void ah_none_loop __P((struct ah_algorithm_state *, const caddr_t, - size_t)); +static void ah_none_loop __P((struct ah_algorithm_state *, caddr_t, size_t)); static void ah_none_result __P((struct ah_algorithm_state *, caddr_t)); static int ah_keyed_md5_mature __P((struct secasvar *)); -static void ah_keyed_md5_init __P((struct ah_algorithm_state *, +static int ah_keyed_md5_init __P((struct ah_algorithm_state *, struct secasvar *)); -static void ah_keyed_md5_loop __P((struct ah_algorithm_state *, const caddr_t, +static void ah_keyed_md5_loop __P((struct ah_algorithm_state *, caddr_t, size_t)); static void ah_keyed_md5_result __P((struct ah_algorithm_state *, caddr_t)); static int ah_keyed_sha1_mature __P((struct secasvar *)); -static void ah_keyed_sha1_init __P((struct ah_algorithm_state *, +static int ah_keyed_sha1_init __P((struct ah_algorithm_state *, struct secasvar *)); -static void ah_keyed_sha1_loop __P((struct ah_algorithm_state *, const caddr_t, +static void ah_keyed_sha1_loop __P((struct ah_algorithm_state *, caddr_t, size_t)); static void ah_keyed_sha1_result __P((struct ah_algorithm_state *, caddr_t)); static int ah_hmac_md5_mature __P((struct secasvar *)); -static void ah_hmac_md5_init __P((struct ah_algorithm_state *, +static int ah_hmac_md5_init __P((struct ah_algorithm_state *, struct secasvar *)); -static void ah_hmac_md5_loop __P((struct ah_algorithm_state *, const caddr_t, +static void ah_hmac_md5_loop __P((struct ah_algorithm_state *, caddr_t, size_t)); static void ah_hmac_md5_result __P((struct ah_algorithm_state *, caddr_t)); static int ah_hmac_sha1_mature __P((struct secasvar *)); -static void ah_hmac_sha1_init __P((struct ah_algorithm_state *, +static int ah_hmac_sha1_init __P((struct ah_algorithm_state *, struct secasvar *)); -static void ah_hmac_sha1_loop __P((struct ah_algorithm_state *, const caddr_t, +static void ah_hmac_sha1_loop __P((struct ah_algorithm_state *, caddr_t, size_t)); static void ah_hmac_sha1_result __P((struct ah_algorithm_state *, caddr_t)); +static void ah_update_mbuf __P((struct mbuf *, int, int, struct ah_algorithm *, + struct ah_algorithm_state *)); + /* checksum algorithms */ -/* NOTE: The order depends on SADB_AALG_x in netkey/keyv2.h */ +/* NOTE: The order depends on SADB_AALG_x in net/pfkeyv2.h */ struct ah_algorithm ah_algorithms[] = { { 0, 0, 0, 0, 0, 0, }, - { ah_sumsiz_1216, ah_hmac_md5_mature, 128, 128, + { ah_sumsiz_1216, ah_hmac_md5_mature, 128, 128, "hmac-md5", ah_hmac_md5_init, ah_hmac_md5_loop, ah_hmac_md5_result, }, - { ah_sumsiz_1216, ah_hmac_sha1_mature, 160, 160, + { ah_sumsiz_1216, ah_hmac_sha1_mature, 160, 160, "hmac-sha1", ah_hmac_sha1_init, ah_hmac_sha1_loop, ah_hmac_sha1_result, }, - { ah_sumsiz_1216, ah_keyed_md5_mature, 128, 128, + { ah_sumsiz_1216, ah_keyed_md5_mature, 128, 128, "keyed-md5", ah_keyed_md5_init, ah_keyed_md5_loop, ah_keyed_md5_result, }, - { ah_sumsiz_1216, ah_keyed_sha1_mature, 160, 160, + { ah_sumsiz_1216, ah_keyed_sha1_mature, 160, 160, "keyed-sha1", ah_keyed_sha1_init, ah_keyed_sha1_loop, ah_keyed_sha1_result, }, - { ah_sumsiz_zero, ah_none_mature, 0, 2048, + { ah_sumsiz_zero, ah_none_mature, 0, 2048, "none", ah_none_init, ah_none_loop, ah_none_result, }, }; static int ah_sumsiz_1216(sav) struct secasvar *sav; { if (!sav) return -1; if (sav->flags & SADB_X_EXT_OLD) return 16; else return 12; } static int ah_sumsiz_zero(sav) struct secasvar *sav; { if (!sav) return -1; return 0; } static int ah_none_mature(sav) struct secasvar *sav; { if (sav->sah->saidx.proto == IPPROTO_AH) { - printf("ah_none_mature: protocol and algorithm mismatch.\n"); + ipseclog((LOG_ERR, + "ah_none_mature: protocol and algorithm mismatch.\n")); return 1; } return 0; } -static void +static int ah_none_init(state, sav) struct ah_algorithm_state *state; struct secasvar *sav; { state->foo = NULL; + return 0; } static void ah_none_loop(state, addr, len) struct ah_algorithm_state *state; - const caddr_t addr; + caddr_t addr; size_t len; { } static void ah_none_result(state, addr) struct ah_algorithm_state *state; caddr_t addr; { } static int ah_keyed_md5_mature(sav) struct secasvar *sav; { /* anything is okay */ return 0; } -static void +static int ah_keyed_md5_init(state, sav) struct ah_algorithm_state *state; struct secasvar *sav; { + size_t padlen; + size_t keybitlen; + u_int8_t buf[32]; + if (!state) panic("ah_keyed_md5_init: what?"); state->sav = sav; state->foo = (void *)malloc(sizeof(MD5_CTX), M_TEMP, M_NOWAIT); if (state->foo == NULL) - panic("ah_keyed_md5_init: what?"); + return ENOBUFS; + MD5Init((MD5_CTX *)state->foo); if (state->sav) { MD5Update((MD5_CTX *)state->foo, (u_int8_t *)_KEYBUF(state->sav->key_auth), (u_int)_KEYLEN(state->sav->key_auth)); - { /* * Pad after the key. * We cannot simply use md5_pad() since the function * won't update the total length. */ - size_t padlen; - size_t keybitlen; - u_int8_t buf[32]; - if (_KEYLEN(state->sav->key_auth) < 56) padlen = 64 - 8 - _KEYLEN(state->sav->key_auth); else padlen = 64 + 64 - 8 - _KEYLEN(state->sav->key_auth); keybitlen = _KEYLEN(state->sav->key_auth); keybitlen *= 8; buf[0] = 0x80; MD5Update((MD5_CTX *)state->foo, &buf[0], 1); padlen--; bzero(buf, sizeof(buf)); while (sizeof(buf) < padlen) { MD5Update((MD5_CTX *)state->foo, &buf[0], sizeof(buf)); padlen -= sizeof(buf); } if (padlen) { MD5Update((MD5_CTX *)state->foo, &buf[0], padlen); } buf[0] = (keybitlen >> 0) & 0xff; buf[1] = (keybitlen >> 8) & 0xff; buf[2] = (keybitlen >> 16) & 0xff; buf[3] = (keybitlen >> 24) & 0xff; MD5Update((MD5_CTX *)state->foo, buf, 8); - } } + + return 0; } static void ah_keyed_md5_loop(state, addr, len) struct ah_algorithm_state *state; - const caddr_t addr; + caddr_t addr; size_t len; { if (!state) panic("ah_keyed_md5_loop: what?"); MD5Update((MD5_CTX *)state->foo, addr, len); } static void ah_keyed_md5_result(state, addr) struct ah_algorithm_state *state; caddr_t addr; { u_char digest[16]; if (!state) panic("ah_keyed_md5_result: what?"); if (state->sav) { MD5Update((MD5_CTX *)state->foo, (u_int8_t *)_KEYBUF(state->sav->key_auth), (u_int)_KEYLEN(state->sav->key_auth)); } MD5Final(&digest[0], (MD5_CTX *)state->foo); free(state->foo, M_TEMP); bcopy(&digest[0], (void *)addr, sizeof(digest)); } static int ah_keyed_sha1_mature(sav) struct secasvar *sav; { struct ah_algorithm *algo; if (!sav->key_auth) { - printf("esp_keyed_sha1_mature: no key is given.\n"); + ipseclog((LOG_ERR, "ah_keyed_sha1_mature: no key is given.\n")); return 1; } algo = &ah_algorithms[sav->alg_auth]; if (sav->key_auth->sadb_key_bits < algo->keymin || algo->keymax < sav->key_auth->sadb_key_bits) { - printf("ah_keyed_sha1_mature: invalid key length %d.\n", - sav->key_auth->sadb_key_bits); + ipseclog((LOG_ERR, + "ah_keyed_sha1_mature: invalid key length %d.\n", + sav->key_auth->sadb_key_bits)); return 1; } return 0; } -static void +static int ah_keyed_sha1_init(state, sav) struct ah_algorithm_state *state; struct secasvar *sav; { SHA1_CTX *ctxt; + size_t padlen; + size_t keybitlen; + u_int8_t buf[32]; if (!state) panic("ah_keyed_sha1_init: what?"); state->sav = sav; state->foo = (void *)malloc(sizeof(SHA1_CTX), M_TEMP, M_NOWAIT); if (!state->foo) - panic("ah_keyed_sha1_init: what?"); + return ENOBUFS; ctxt = (SHA1_CTX *)state->foo; SHA1Init(ctxt); if (state->sav) { SHA1Update(ctxt, (u_int8_t *)_KEYBUF(state->sav->key_auth), (u_int)_KEYLEN(state->sav->key_auth)); - { /* * Pad after the key. */ - size_t padlen; - size_t keybitlen; - u_int8_t buf[32]; - if (_KEYLEN(state->sav->key_auth) < 56) padlen = 64 - 8 - _KEYLEN(state->sav->key_auth); else padlen = 64 + 64 - 8 - _KEYLEN(state->sav->key_auth); keybitlen = _KEYLEN(state->sav->key_auth); keybitlen *= 8; buf[0] = 0x80; SHA1Update(ctxt, &buf[0], 1); padlen--; bzero(buf, sizeof(buf)); while (sizeof(buf) < padlen) { SHA1Update(ctxt, &buf[0], sizeof(buf)); padlen -= sizeof(buf); } if (padlen) { SHA1Update(ctxt, &buf[0], padlen); } buf[0] = (keybitlen >> 0) & 0xff; buf[1] = (keybitlen >> 8) & 0xff; buf[2] = (keybitlen >> 16) & 0xff; buf[3] = (keybitlen >> 24) & 0xff; SHA1Update(ctxt, buf, 8); - } } + + return 0; } static void ah_keyed_sha1_loop(state, addr, len) struct ah_algorithm_state *state; - const caddr_t addr; + caddr_t addr; size_t len; { SHA1_CTX *ctxt; if (!state || !state->foo) panic("ah_keyed_sha1_loop: what?"); ctxt = (SHA1_CTX *)state->foo; - sha1_loop(ctxt, (caddr_t)addr, (size_t)len); + SHA1Update(ctxt, (caddr_t)addr, (size_t)len); } static void ah_keyed_sha1_result(state, addr) struct ah_algorithm_state *state; caddr_t addr; { u_char digest[SHA1_RESULTLEN]; /* SHA-1 generates 160 bits */ SHA1_CTX *ctxt; if (!state || !state->foo) panic("ah_keyed_sha1_result: what?"); ctxt = (SHA1_CTX *)state->foo; if (state->sav) { SHA1Update(ctxt, (u_int8_t *)_KEYBUF(state->sav->key_auth), (u_int)_KEYLEN(state->sav->key_auth)); } SHA1Final((caddr_t)&digest[0], ctxt); bcopy(&digest[0], (void *)addr, HMACSIZE); free(state->foo, M_TEMP); } static int ah_hmac_md5_mature(sav) struct secasvar *sav; { struct ah_algorithm *algo; if (!sav->key_auth) { - printf("esp_hmac_md5_mature: no key is given.\n"); + ipseclog((LOG_ERR, "ah_hmac_md5_mature: no key is given.\n")); return 1; } algo = &ah_algorithms[sav->alg_auth]; if (sav->key_auth->sadb_key_bits < algo->keymin || algo->keymax < sav->key_auth->sadb_key_bits) { - printf("ah_hmac_md5_mature: invalid key length %d.\n", - sav->key_auth->sadb_key_bits); + ipseclog((LOG_ERR, + "ah_hmac_md5_mature: invalid key length %d.\n", + sav->key_auth->sadb_key_bits)); return 1; } return 0; } -static void +static int ah_hmac_md5_init(state, sav) struct ah_algorithm_state *state; struct secasvar *sav; { u_char *ipad; u_char *opad; u_char tk[16]; u_char *key; size_t keylen; size_t i; MD5_CTX *ctxt; if (!state) panic("ah_hmac_md5_init: what?"); state->sav = sav; state->foo = (void *)malloc(64 + 64 + sizeof(MD5_CTX), M_TEMP, M_NOWAIT); if (!state->foo) - panic("ah_hmac_md5_init: what?"); + return ENOBUFS; ipad = (u_char *)state->foo; opad = (u_char *)(ipad + 64); ctxt = (MD5_CTX *)(opad + 64); /* compress the key if necessery */ if (64 < _KEYLEN(state->sav->key_auth)) { MD5Init(ctxt); MD5Update(ctxt, _KEYBUF(state->sav->key_auth), _KEYLEN(state->sav->key_auth)); MD5Final(&tk[0], ctxt); key = &tk[0]; keylen = 16; } else { key = _KEYBUF(state->sav->key_auth); keylen = _KEYLEN(state->sav->key_auth); } bzero(ipad, 64); bzero(opad, 64); bcopy(key, ipad, keylen); bcopy(key, opad, keylen); for (i = 0; i < 64; i++) { ipad[i] ^= 0x36; opad[i] ^= 0x5c; } MD5Init(ctxt); MD5Update(ctxt, ipad, 64); + + return 0; } static void ah_hmac_md5_loop(state, addr, len) struct ah_algorithm_state *state; - const caddr_t addr; + caddr_t addr; size_t len; { MD5_CTX *ctxt; if (!state || !state->foo) panic("ah_hmac_md5_loop: what?"); ctxt = (MD5_CTX *)(((caddr_t)state->foo) + 128); MD5Update(ctxt, addr, len); } static void ah_hmac_md5_result(state, addr) struct ah_algorithm_state *state; caddr_t addr; { u_char digest[16]; u_char *ipad; u_char *opad; MD5_CTX *ctxt; if (!state || !state->foo) panic("ah_hmac_md5_result: what?"); ipad = (u_char *)state->foo; opad = (u_char *)(ipad + 64); ctxt = (MD5_CTX *)(opad + 64); MD5Final(&digest[0], ctxt); MD5Init(ctxt); MD5Update(ctxt, opad, 64); MD5Update(ctxt, &digest[0], sizeof(digest)); MD5Final(&digest[0], ctxt); bcopy(&digest[0], (void *)addr, HMACSIZE); free(state->foo, M_TEMP); } static int ah_hmac_sha1_mature(sav) struct secasvar *sav; { struct ah_algorithm *algo; if (!sav->key_auth) { - printf("esp_hmac_sha1_mature: no key is given.\n"); + ipseclog((LOG_ERR, "ah_hmac_sha1_mature: no key is given.\n")); return 1; } algo = &ah_algorithms[sav->alg_auth]; if (sav->key_auth->sadb_key_bits < algo->keymin || algo->keymax < sav->key_auth->sadb_key_bits) { - printf("ah_hmac_sha1_mature: invalid key length %d.\n", - sav->key_auth->sadb_key_bits); + ipseclog((LOG_ERR, + "ah_hmac_sha1_mature: invalid key length %d.\n", + sav->key_auth->sadb_key_bits)); return 1; } return 0; } -static void +static int ah_hmac_sha1_init(state, sav) struct ah_algorithm_state *state; struct secasvar *sav; { u_char *ipad; u_char *opad; SHA1_CTX *ctxt; u_char tk[SHA1_RESULTLEN]; /* SHA-1 generates 160 bits */ u_char *key; size_t keylen; size_t i; if (!state) panic("ah_hmac_sha1_init: what?"); state->sav = sav; state->foo = (void *)malloc(64 + 64 + sizeof(SHA1_CTX), M_TEMP, M_NOWAIT); if (!state->foo) - panic("ah_hmac_sha1_init: what?"); + return ENOBUFS; ipad = (u_char *)state->foo; opad = (u_char *)(ipad + 64); ctxt = (SHA1_CTX *)(opad + 64); /* compress the key if necessery */ if (64 < _KEYLEN(state->sav->key_auth)) { SHA1Init(ctxt); SHA1Update(ctxt, _KEYBUF(state->sav->key_auth), _KEYLEN(state->sav->key_auth)); SHA1Final(&tk[0], ctxt); key = &tk[0]; keylen = SHA1_RESULTLEN; } else { key = _KEYBUF(state->sav->key_auth); keylen = _KEYLEN(state->sav->key_auth); } bzero(ipad, 64); bzero(opad, 64); bcopy(key, ipad, keylen); bcopy(key, opad, keylen); for (i = 0; i < 64; i++) { ipad[i] ^= 0x36; opad[i] ^= 0x5c; } SHA1Init(ctxt); SHA1Update(ctxt, ipad, 64); + + return 0; } static void ah_hmac_sha1_loop(state, addr, len) struct ah_algorithm_state *state; - const caddr_t addr; + caddr_t addr; size_t len; { SHA1_CTX *ctxt; if (!state || !state->foo) panic("ah_hmac_sha1_loop: what?"); ctxt = (SHA1_CTX *)(((u_char *)state->foo) + 128); SHA1Update(ctxt, (caddr_t)addr, (size_t)len); } static void ah_hmac_sha1_result(state, addr) struct ah_algorithm_state *state; caddr_t addr; { u_char digest[SHA1_RESULTLEN]; /* SHA-1 generates 160 bits */ u_char *ipad; u_char *opad; SHA1_CTX *ctxt; if (!state || !state->foo) panic("ah_hmac_sha1_result: what?"); ipad = (u_char *)state->foo; opad = (u_char *)(ipad + 64); ctxt = (SHA1_CTX *)(opad + 64); SHA1Final((caddr_t)&digest[0], ctxt); SHA1Init(ctxt); SHA1Update(ctxt, opad, 64); SHA1Update(ctxt, (caddr_t)&digest[0], sizeof(digest)); SHA1Final((caddr_t)&digest[0], ctxt); bcopy(&digest[0], (void *)addr, HMACSIZE); free(state->foo, M_TEMP); } /*------------------------------------------------------------*/ /* * go generate the checksum. */ +static void +ah_update_mbuf(m, off, len, algo, algos) + struct mbuf *m; + int off; + int len; + struct ah_algorithm *algo; + struct ah_algorithm_state *algos; +{ + struct mbuf *n; + int tlen; + + /* easy case first */ + if (off + len <= m->m_len) { + (algo->update)(algos, mtod(m, caddr_t) + off, len); + return; + } + + for (n = m; n; n = n->m_next) { + if (off < n->m_len) + break; + + off -= n->m_len; + } + + if (!n) + panic("ah_update_mbuf: wrong offset specified"); + + for (/*nothing*/; n && len > 0; n = n->m_next) { + if (n->m_len == 0) + continue; + if (n->m_len - off < len) + tlen = n->m_len - off; + else + tlen = len; + + (algo->update)(algos, mtod(n, caddr_t) + off, tlen); + + len -= tlen; + off = 0; + } +} + +/* + * Go generate the checksum. This function won't modify the mbuf chain + * except AH itself. + * + * NOTE: the function does not free mbuf on failure. + * Don't use m_copy(), it will try to share cluster mbuf by using refcnt. + */ int -ah4_calccksum(m0, ahdat, algo, sav) - struct mbuf *m0; +ah4_calccksum(m, ahdat, len, algo, sav) + struct mbuf *m; caddr_t ahdat; + size_t len; struct ah_algorithm *algo; struct secasvar *sav; { - struct mbuf *m; + int off; int hdrtype; - u_char *p; size_t advancewidth; struct ah_algorithm_state algos; - int tlen; u_char sumbuf[AH_MAXSUMSIZE]; int error = 0; + int ahseen; + struct mbuf *n = NULL; + if ((m->m_flags & M_PKTHDR) == 0) + return EINVAL; + + ahseen = 0; hdrtype = -1; /*dummy, it is called IPPROTO_IP*/ - m = m0; + off = 0; - p = mtod(m, u_char *); + error = (algo->init)(&algos, sav); + if (error) + return error; - (algo->init)(&algos, sav); - advancewidth = 0; /*safety*/ again: /* gory. */ switch (hdrtype) { - case -1: /*first one*/ + case -1: /*first one only*/ { /* * copy ip hdr, modify to fit the AH checksum rule, * then take a checksum. - * XXX need to care about source routing... jesus. */ struct ip iphdr; size_t hlen; - bcopy((caddr_t)p, (caddr_t)&iphdr, sizeof(struct ip)); + m_copydata(m, off, sizeof(iphdr), (caddr_t)&iphdr); #ifdef _IP_VHL hlen = IP_VHL_HL(iphdr.ip_vhl) << 2; #else hlen = iphdr.ip_hl << 2; #endif iphdr.ip_ttl = 0; iphdr.ip_sum = htons(0); - if (ip4_ah_cleartos) iphdr.ip_tos = 0; + if (ip4_ah_cleartos) + iphdr.ip_tos = 0; iphdr.ip_off = htons(ntohs(iphdr.ip_off) & ip4_ah_offsetmask); (algo->update)(&algos, (caddr_t)&iphdr, sizeof(struct ip)); if (hlen != sizeof(struct ip)) { u_char *p; - int i, j; - int l, skip; - u_char dummy[4]; + int i, l, skip; + if (hlen > MCLBYTES) { + error = EMSGSIZE; + goto fail; + } + MGET(n, M_DONTWAIT, MT_DATA); + if (n && hlen > MLEN) { + MCLGET(n, M_DONTWAIT); + if ((n->m_flags & M_EXT) == 0) { + m_free(n); + n = NULL; + } + } + if (n == NULL) { + error = ENOBUFS; + goto fail; + } + m_copydata(m, off, hlen, mtod(n, caddr_t)); + /* * IP options processing. * See RFC2402 appendix A. */ - bzero(dummy, sizeof(dummy)); - p = mtod(m, u_char *); + p = mtod(n, u_char *); i = sizeof(struct ip); while (i < hlen) { skip = 1; switch (p[i + IPOPT_OPTVAL]) { case IPOPT_EOL: case IPOPT_NOP: l = 1; skip = 0; break; case IPOPT_SECURITY: /* 0x82 */ case 0x85: /* Extended security */ case 0x86: /* Commercial security */ case 0x94: /* Router alert */ case 0x95: /* RFC1770 */ l = p[i + IPOPT_OLEN]; skip = 0; break; default: l = p[i + IPOPT_OLEN]; skip = 1; break; } if (l <= 0 || hlen - i < l) { - printf("ah4_input: invalid IP option " - "(type=%02x len=%02x)\n", - p[i + IPOPT_OPTVAL], - p[i + IPOPT_OLEN]); - break; + ipseclog((LOG_ERR, + "ah4_calccksum: invalid IP option " + "(type=%02x len=%02x)\n", + p[i + IPOPT_OPTVAL], + p[i + IPOPT_OLEN])); + m_free(n); + n = NULL; + error = EINVAL; + goto fail; } - if (skip) { - for (j = 0; j < l / sizeof(dummy); j++) - (algo->update)(&algos, dummy, sizeof(dummy)); - - (algo->update)(&algos, dummy, l % sizeof(dummy)); - } else - (algo->update)(&algos, p + i, l); + if (skip) + bzero(p + i, l); if (p[i + IPOPT_OPTVAL] == IPOPT_EOL) break; i += l; } + p = mtod(n, u_char *) + sizeof(struct ip); + (algo->update)(&algos, p, hlen - sizeof(struct ip)); + + m_free(n); + n = NULL; } hdrtype = (iphdr.ip_p) & 0xff; advancewidth = hlen; break; } case IPPROTO_AH: { - u_char dummy[4]; + struct ah ah; int siz; int hdrsiz; + int totlen; - hdrsiz = (sav->flags & SADB_X_EXT_OLD) ? - sizeof(struct ah) : sizeof(struct newah); - - (algo->update)(&algos, p, hdrsiz); - - /* key data region. */ + m_copydata(m, off, sizeof(ah), (caddr_t)&ah); + hdrsiz = (sav->flags & SADB_X_EXT_OLD) + ? sizeof(struct ah) + : sizeof(struct newah); siz = (*algo->sumsiz)(sav); - bzero(&dummy[0], sizeof(dummy)); - while (sizeof(dummy) <= siz) { - (algo->update)(&algos, dummy, sizeof(dummy)); - siz -= sizeof(dummy); - } - /* can't happen, but just in case */ - if (siz) - (algo->update)(&algos, dummy, siz); + totlen = (ah.ah_len + 2) << 2; - /* padding region, just in case */ - siz = (((struct ah *)p)->ah_len << 2) - (*algo->sumsiz)(sav); - if ((sav->flags & SADB_X_EXT_OLD) == 0) - siz -= 4; /* sequence number field */ - if (0 < siz) { - /* RFC 1826 */ - (algo->update)(&algos, p + hdrsiz + (*algo->sumsiz)(sav), - siz); - } + /* + * special treatment is necessary for the first one, not others + */ + if (!ahseen) { + if (totlen > m->m_pkthdr.len - off || + totlen > MCLBYTES) { + error = EMSGSIZE; + goto fail; + } + MGET(n, M_DONTWAIT, MT_DATA); + if (n && totlen > MLEN) { + MCLGET(n, M_DONTWAIT); + if ((n->m_flags & M_EXT) == 0) { + m_free(n); + n = NULL; + } + } + if (n == NULL) { + error = ENOBUFS; + goto fail; + } + m_copydata(m, off, totlen, mtod(n, caddr_t)); + n->m_len = totlen; + bzero(mtod(n, caddr_t) + hdrsiz, siz); + (algo->update)(&algos, mtod(n, caddr_t), n->m_len); + m_free(n); + n = NULL; + } else + ah_update_mbuf(m, off, totlen, algo, &algos); + ahseen++; - hdrtype = ((struct ah *)p)->ah_nxt; - advancewidth = hdrsiz; - advancewidth += ((struct ah *)p)->ah_len << 2; - if ((sav->flags & SADB_X_EXT_OLD) == 0) - advancewidth -= 4; /* sequence number field */ + hdrtype = ah.ah_nxt; + advancewidth = totlen; break; } default: - printf("ah4_calccksum: unexpected hdrtype=%x; " - "treating rest as payload\n", hdrtype); - /*fall through*/ - case IPPROTO_ICMP: - case IPPROTO_IGMP: - case IPPROTO_IPIP: -#ifdef INET6 - case IPPROTO_IPV6: - case IPPROTO_ICMPV6: -#endif - case IPPROTO_UDP: - case IPPROTO_TCP: - case IPPROTO_ESP: - while (m) { - tlen = m->m_len - (p - mtod(m, u_char *)); - (algo->update)(&algos, p, tlen); - m = m->m_next; - p = m ? mtod(m, u_char *) : NULL; - } - - advancewidth = 0; /*loop finished*/ + ah_update_mbuf(m, off, m->m_pkthdr.len - off, algo, &algos); + advancewidth = m->m_pkthdr.len - off; break; } - if (advancewidth) { - /* is it safe? */ - while (m && advancewidth) { - tlen = m->m_len - (p - mtod(m, u_char *)); - if (advancewidth < tlen) { - p += advancewidth; - advancewidth = 0; - } else { - advancewidth -= tlen; - m = m->m_next; - if (m) - p = mtod(m, u_char *); - else { - printf("ERR: hit the end-of-mbuf...\n"); - p = NULL; - } - } - } + off += advancewidth; + if (off < m->m_pkthdr.len) + goto again; - if (m) - goto again; + if (len < (*algo->sumsiz)(sav)) { + error = EINVAL; + goto fail; } - /* for HMAC algorithms... */ (algo->result)(&algos, &sumbuf[0]); bcopy(&sumbuf[0], ahdat, (*algo->sumsiz)(sav)); + if (n) + m_free(n); return error; + +fail: + if (n) + m_free(n); + return error; } #ifdef INET6 /* - * go generate the checksum. This function won't modify the mbuf chain + * Go generate the checksum. This function won't modify the mbuf chain * except AH itself. + * + * NOTE: the function does not free mbuf on failure. + * Don't use m_copy(), it will try to share cluster mbuf by using refcnt. */ int -ah6_calccksum(m0, ahdat, algo, sav) - struct mbuf *m0; +ah6_calccksum(m, ahdat, len, algo, sav) + struct mbuf *m; caddr_t ahdat; + size_t len; struct ah_algorithm *algo; struct secasvar *sav; { - struct mbuf *m; - int hdrtype; - u_char *p; - size_t advancewidth; + int newoff, off; + int proto, nxt; + struct mbuf *n = NULL; + int error; + int ahseen; struct ah_algorithm_state algos; - int tlen; - int error = 0; u_char sumbuf[AH_MAXSUMSIZE]; - int nest; - hdrtype = -1; /*dummy, it is called IPPROTO_IPV6 */ + if ((m->m_flags & M_PKTHDR) == 0) + return EINVAL; - m = m0; + error = (algo->init)(&algos, sav); + if (error) + return error; - p = mtod(m, u_char *); + off = 0; + proto = IPPROTO_IPV6; + nxt = -1; + ahseen = 0; - (algo->init)(&algos, sav); - - advancewidth = 0; /*safety*/ - nest = 0; - -again: - if (ip6_hdrnestlimit && (++nest > ip6_hdrnestlimit)) { - ip6stat.ip6s_toomanyhdr++; - error = EINVAL; /*XXX*/ - goto bad; + again: + newoff = ip6_nexthdr(m, off, proto, &nxt); + if (newoff < 0) + newoff = m->m_pkthdr.len; + else if (newoff <= off) { + error = EINVAL; + goto fail; } - /* gory. */ - switch (hdrtype) { - case -1: /*first one*/ - { - struct ip6_hdr ip6copy; + switch (proto) { + case IPPROTO_IPV6: + /* + * special treatment is necessary for the first one, not others + */ + if (off == 0) { + struct ip6_hdr ip6copy; - bcopy(p, &ip6copy, sizeof(struct ip6_hdr)); - /* RFC2402 */ - ip6copy.ip6_flow = 0; - ip6copy.ip6_vfc = IPV6_VERSION; - ip6copy.ip6_hlim = 0; - if (IN6_IS_ADDR_LINKLOCAL(&ip6copy.ip6_src)) - ip6copy.ip6_src.s6_addr16[1] = 0x0000; - if (IN6_IS_ADDR_LINKLOCAL(&ip6copy.ip6_dst)) - ip6copy.ip6_dst.s6_addr16[1] = 0x0000; - (algo->update)(&algos, (caddr_t)&ip6copy, - sizeof(struct ip6_hdr)); - hdrtype = (((struct ip6_hdr *)p)->ip6_nxt) & 0xff; - advancewidth = sizeof(struct ip6_hdr); + if (newoff - off != sizeof(struct ip6_hdr)) { + error = EINVAL; + goto fail; + } + + m_copydata(m, off, newoff - off, (caddr_t)&ip6copy); + /* RFC2402 */ + ip6copy.ip6_flow = 0; + ip6copy.ip6_vfc &= ~IPV6_VERSION_MASK; + ip6copy.ip6_vfc |= IPV6_VERSION; + ip6copy.ip6_hlim = 0; + if (IN6_IS_ADDR_LINKLOCAL(&ip6copy.ip6_src)) + ip6copy.ip6_src.s6_addr16[1] = 0x0000; + if (IN6_IS_ADDR_LINKLOCAL(&ip6copy.ip6_dst)) + ip6copy.ip6_dst.s6_addr16[1] = 0x0000; + (algo->update)(&algos, (caddr_t)&ip6copy, + sizeof(struct ip6_hdr)); + } else { + newoff = m->m_pkthdr.len; + ah_update_mbuf(m, off, m->m_pkthdr.len - off, algo, + &algos); + } break; - } case IPPROTO_AH: { - u_char dummy[4]; int siz; int hdrsiz; - hdrsiz = (sav->flags & SADB_X_EXT_OLD) ? - sizeof(struct ah) : sizeof(struct newah); - - (algo->update)(&algos, p, hdrsiz); - - /* key data region. */ + hdrsiz = (sav->flags & SADB_X_EXT_OLD) + ? sizeof(struct ah) + : sizeof(struct newah); siz = (*algo->sumsiz)(sav); - bzero(&dummy[0], 4); - while (4 <= siz) { - (algo->update)(&algos, dummy, 4); - siz -= 4; - } - /* can't happen, but just in case */ - if (siz) - (algo->update)(&algos, dummy, siz); - /* padding region, just in case */ - siz = (((struct ah *)p)->ah_len << 2) - (*algo->sumsiz)(sav); - if ((sav->flags & SADB_X_EXT_OLD) == 0) - siz -= 4; /* sequence number field */ - if (0 < siz) { - (algo->update)(&algos, p + hdrsiz + (*algo->sumsiz)(sav), - siz); - } - - hdrtype = ((struct ah *)p)->ah_nxt; - advancewidth = hdrsiz; - advancewidth += ((struct ah *)p)->ah_len << 2; - if ((sav->flags & SADB_X_EXT_OLD) == 0) - advancewidth -= 4; /* sequence number field */ + /* + * special treatment is necessary for the first one, not others + */ + if (!ahseen) { + if (newoff - off > MCLBYTES) { + error = EMSGSIZE; + goto fail; + } + MGET(n, M_DONTWAIT, MT_DATA); + if (n && newoff - off > MLEN) { + MCLGET(n, M_DONTWAIT); + if ((n->m_flags & M_EXT) == 0) { + m_free(n); + n = NULL; + } + } + if (n == NULL) { + error = ENOBUFS; + goto fail; + } + m_copydata(m, off, newoff - off, mtod(n, caddr_t)); + n->m_len = newoff - off; + bzero(mtod(n, caddr_t) + hdrsiz, siz); + (algo->update)(&algos, mtod(n, caddr_t), n->m_len); + m_free(n); + n = NULL; + } else + ah_update_mbuf(m, off, newoff - off, algo, &algos); + ahseen++; break; } case IPPROTO_HOPOPTS: case IPPROTO_DSTOPTS: { - int hdrlen, optlen; - u_int8_t *optp, *lastp = p, *optend, opt; + struct ip6_ext *ip6e; + int hdrlen, optlen; + u_int8_t *p, *optend, *optp; - tlen = m->m_len - (p - mtod(m, u_char *)); - /* We assume all the options is contained in a single mbuf */ - if (tlen < sizeof(struct ip6_ext)) { + if (newoff - off > MCLBYTES) { + error = EMSGSIZE; + goto fail; + } + MGET(n, M_DONTWAIT, MT_DATA); + if (n && newoff - off > MLEN) { + MCLGET(n, M_DONTWAIT); + if ((n->m_flags & M_EXT) == 0) { + m_free(n); + n = NULL; + } + } + if (n == NULL) { + error = ENOBUFS; + goto fail; + } + m_copydata(m, off, newoff - off, mtod(n, caddr_t)); + n->m_len = newoff - off; + + ip6e = mtod(n, struct ip6_ext *); + hdrlen = (ip6e->ip6e_len + 1) << 3; + if (newoff - off < hdrlen) { error = EINVAL; - goto bad; - } - hdrlen = (((struct ip6_ext *)p)->ip6e_len + 1) << 3; - hdrtype = (int)((struct ip6_ext *)p)->ip6e_nxt; - if (tlen < hdrlen) { - error = EINVAL; - goto bad; - } - optend = p + hdrlen; + m_free(n); + n = NULL; + goto fail; + } + p = mtod(n, u_int8_t *); + optend = p + hdrlen; - /* - * ICV calculation for the options header including all - * options. This part is a little tricky since there are - * two type of options; mutable and immutable. Our approach - * is to calculate ICV for a consecutive immutable options - * at once. Here is an example. In the following figure, - * suppose that we've calculated ICV from the top of the - * header to MutableOpt1, which is a mutable option. - * lastp points to the end of MutableOpt1. Some immutable - * options follows MutableOpt1, and we encounter a new - * mutable option; MutableOpt2. optp points to the head - * of MutableOpt2. In this situation, uncalculated immutable - * field is the field from lastp to optp+2 (note that the - * type and the length fields are considered as immutable - * even in a mutable option). So we first calculate ICV - * for the field as immutable, then calculate from optp+2 - * to the end of MutableOpt2, whose length is optlen-2, - * where optlen is the length of MutableOpt2. Finally, - * lastp is updated to point to the end of MutableOpt2 - * for further calculation. The updated point is shown as - * lastp' in the figure. - * <------ optlen -----> - * -----------+-------------------+---+---+-----------+ - * MutableOpt1|ImmutableOptions...|typ|len|MutableOpt2| - * -----------+-------------------+---+---+-----------+ - * ^ ^ ^ - * lastp optp optp+2 - * <---- optp + 2 - lastp -----><-optlen-2-> - * ^ - * lastp' - */ - for (optp = p + 2; optp < optend; optp += optlen) { - opt = optp[0]; - if (opt == IP6OPT_PAD1) { - optlen = 1; - } else { - if (optp + 2 > optend) { - error = EINVAL; /* malformed option */ - goto bad; - } - optlen = optp[1] + 2; - if (opt & IP6OPT_MUTABLE) { - /* - * ICV calc. for the (consecutive) - * immutable field followd by the - * option. - */ - (algo->update)(&algos, lastp, - optp + 2 - lastp); - if (optlen - 2 > ZEROBUFLEN) { - error = EINVAL; /* XXX */ - goto bad; - } - /* - * ICV calc. for the mutable - * option using an all-0 buffer. - */ - (algo->update)(&algos, zerobuf, - optlen - 2); - lastp = optp + optlen; - } - } - } - /* - * Wrap up the calulation; compute ICV for the consecutive - * immutable options at the end of the header(if any). - */ - (algo->update)(&algos, lastp, p + hdrlen - lastp); - advancewidth = hdrlen; - break; + /* + * ICV calculation for the options header including all + * options. This part is a little tricky since there are + * two type of options; mutable and immutable. We try to + * null-out mutable ones here. + */ + optp = p + 2; + while (optp < optend) { + if (optp[0] == IP6OPT_PAD1) + optlen = 1; + else { + if (optp + 2 > optend) { + error = EINVAL; + m_free(n); + n = NULL; + goto fail; + } + optlen = optp[1] + 2; + + if (optp[0] & IP6OPT_MUTABLE) + bzero(optp + 2, optlen - 2); + } + + optp += optlen; + } + + (algo->update)(&algos, mtod(n, caddr_t), n->m_len); + m_free(n); + n = NULL; + break; } + case IPPROTO_ROUTING: - { - /* - * For an input packet, we can just calculate `as is'. - * For an output packet, we assume ip6_output have already - * made packet how it will be received at the final destination. - * So we'll only check if the header is malformed. - */ - int hdrlen; + /* + * For an input packet, we can just calculate `as is'. + * For an output packet, we assume ip6_output have already + * made packet how it will be received at the final + * destination. + */ + /* FALLTHROUGH */ - tlen = m->m_len - (p - mtod(m, u_char *)); - /* We assume all the options is contained in a single mbuf */ - if (tlen < sizeof(struct ip6_ext)) { - error = EINVAL; - goto bad; - } - hdrlen = (((struct ip6_ext *)p)->ip6e_len + 1) << 3; - hdrtype = (int)((struct ip6_ext *)p)->ip6e_nxt; - if (tlen < hdrlen) { - error = EINVAL; - goto bad; - } - advancewidth = hdrlen; - (algo->update)(&algos, p, hdrlen); - break; - } default: - printf("ah6_calccksum: unexpected hdrtype=%x; " - "treating rest as payload\n", hdrtype); - /*fall through*/ - case IPPROTO_ICMP: - case IPPROTO_IGMP: - case IPPROTO_IPIP: /*?*/ - case IPPROTO_IPV6: - case IPPROTO_ICMPV6: - case IPPROTO_UDP: - case IPPROTO_TCP: - case IPPROTO_ESP: - while (m) { - tlen = m->m_len - (p - mtod(m, u_char *)); - (algo->update)(&algos, p, tlen); - m = m->m_next; - p = m ? mtod(m, u_char *) : NULL; - } - - advancewidth = 0; /*loop finished*/ + ah_update_mbuf(m, off, newoff - off, algo, &algos); break; } - if (advancewidth) { - /* is it safe? */ - while (m && advancewidth) { - tlen = m->m_len - (p - mtod(m, u_char *)); - if (advancewidth < tlen) { - p += advancewidth; - advancewidth = 0; - } else { - advancewidth -= tlen; - m = m->m_next; - if (m) - p = mtod(m, u_char *); - else { - printf("ERR: hit the end-of-mbuf...\n"); - p = NULL; - } - } - } + if (newoff < m->m_pkthdr.len) { + proto = nxt; + off = newoff; + goto again; + } - if (m) - goto again; + if (len < (*algo->sumsiz)(sav)) { + error = EINVAL; + goto fail; } - /* for HMAC algorithms... */ (algo->result)(&algos, &sumbuf[0]); bcopy(&sumbuf[0], ahdat, (*algo->sumsiz)(sav)); - return(0); - - bad: - return(error); + /* just in case */ + if (n) + m_free(n); + return 0; +fail: + /* just in case */ + if (n) + m_free(n); + return error; } #endif Index: head/sys/netinet6/ah_input.c =================================================================== --- head/sys/netinet6/ah_input.c (revision 62586) +++ head/sys/netinet6/ah_input.c (revision 62587) @@ -1,707 +1,978 @@ +/* $FreeBSD$ */ +/* $KAME: ah_input.c,v 1.29 2000/05/29 08:33:53 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* * RFC1826/2402 authentication header. */ #include "opt_inet.h" #include "opt_inet6.h" -#include "opt_ipsec.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef INET6 #include #endif #ifdef INET6 -#include +#include #include -#include +#include #endif #include -#include #ifdef INET6 #include +#endif +#include +#ifdef INET6 #include #endif #include #include #ifdef IPSEC_DEBUG #include #else #define KEYDEBUG(lev,arg) #endif -#include - #include #include +#define IPLEN_FLIPPED + #ifdef INET +#include extern struct ipprotosw inetsw[]; void -ah4_input(m, off, proto) +#if __STDC__ +ah4_input(struct mbuf *m, ...) +#else +ah4_input(m, va_alist) struct mbuf *m; - int off, proto; + va_dcl +#endif { struct ip *ip; struct ah *ah; u_int32_t spi; struct ah_algorithm *algo; size_t siz; size_t siz1; u_char *cksum; struct secasvar *sav = NULL; u_int16_t nxt; size_t hlen; int s; + int off, proto; + va_list ap; + va_start(ap, m); + off = va_arg(ap, int); + proto = va_arg(ap, int); + va_end(ap); + +#ifndef PULLDOWN_TEST if (m->m_len < off + sizeof(struct newah)) { m = m_pullup(m, off + sizeof(struct newah)); if (!m) { - printf("IPv4 AH input: can't pullup;" - "dropping the packet for simplicity\n"); + ipseclog((LOG_DEBUG, "IPv4 AH input: can't pullup;" + "dropping the packet for simplicity\n")); ipsecstat.in_inval++; goto fail; } } ip = mtod(m, struct ip *); ah = (struct ah *)(((caddr_t)ip) + off); +#else + ip = mtod(m, struct ip *); + IP6_EXTHDR_GET(ah, struct ah *, m, off, sizeof(struct newah)); + if (ah == NULL) { + ipseclog((LOG_DEBUG, "IPv4 AH input: can't pullup;" + "dropping the packet for simplicity\n")); + ipsecstat.in_inval++; + goto fail; + } +#endif nxt = ah->ah_nxt; #ifdef _IP_VHL hlen = IP_VHL_HL(ip->ip_vhl) << 2; #else hlen = ip->ip_hl << 2; #endif /* find the sassoc. */ spi = ah->ah_spi; if ((sav = key_allocsa(AF_INET, (caddr_t)&ip->ip_src, (caddr_t)&ip->ip_dst, IPPROTO_AH, spi)) == 0) { - printf("IPv4 AH input: no key association found for spi %u;" - "dropping the packet for simplicity\n", - (u_int32_t)ntohl(spi)); + ipseclog((LOG_WARNING, + "IPv4 AH input: no key association found for spi %u\n", + (u_int32_t)ntohl(spi))); ipsecstat.in_nosa++; goto fail; } KEYDEBUG(KEYDEBUG_IPSEC_STAMP, printf("DP ah4_input called to allocate SA:%p\n", sav)); if (sav->state != SADB_SASTATE_MATURE && sav->state != SADB_SASTATE_DYING) { - printf("IPv4 AH input: non-mature/dying SA found for spi %u; " - "dropping the packet for simplicity\n", - (u_int32_t)ntohl(spi)); + ipseclog((LOG_DEBUG, + "IPv4 AH input: non-mature/dying SA found for spi %u\n", + (u_int32_t)ntohl(spi))); ipsecstat.in_badspi++; goto fail; } if (sav->alg_auth == SADB_AALG_NONE) { - printf("IPv4 AH input: unspecified authentication algorithm " - "for spi %u;" - "dropping the packet for simplicity\n", - (u_int32_t)ntohl(spi)); + ipseclog((LOG_DEBUG, "IPv4 AH input: " + "unspecified authentication algorithm for spi %u\n", + (u_int32_t)ntohl(spi))); ipsecstat.in_badspi++; goto fail; } algo = &ah_algorithms[sav->alg_auth]; siz = (*algo->sumsiz)(sav); siz1 = ((siz + 3) & ~(4 - 1)); /* * sanity checks for header, 1. */ { int sizoff; sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4; + /* + * Here, we do not do "siz1 == siz". This is because the way + * RFC240[34] section 2 is written. They do not require truncation + * to 96 bits. + * For example, Microsoft IPsec stack attaches 160 bits of + * authentication data for both hmac-md5 and hmac-sha1. For hmac-sha1, + * 32 bits of padding is attached. + * + * There are two downsides to this specification. + * They have no real harm, however, they leave us fuzzy feeling. + * - if we attach more than 96 bits of authentication data onto AH, + * we will never notice about possible modification by rogue + * intermediate nodes. + * Since extra bits in AH checksum is never used, this constitutes + * no real issue, however, it is wacky. + * - even if the peer attaches big authentication data, we will never + * notice the difference, since longer authentication data will just + * work. + * + * We may need some clarification in the spec. + */ + if (siz1 < siz) { + ipseclog((LOG_NOTICE, "sum length too short in IPv4 AH input " + "(%lu, should be at least %lu): %s\n", + (u_long)siz1, (u_long)siz, + ipsec4_logpacketstr(ip, spi))); + ipsecstat.in_inval++; + goto fail; + } if ((ah->ah_len << 2) - sizoff != siz1) { - log(LOG_NOTICE, "sum length mismatch in IPv4 AH input " - "(%d should be %u): %s\n", - (ah->ah_len << 2) - sizoff, (unsigned int)siz1, - ipsec4_logpacketstr(ip, spi)); + ipseclog((LOG_NOTICE, "sum length mismatch in IPv4 AH input " + "(%d should be %lu): %s\n", + (ah->ah_len << 2) - sizoff, (u_long)siz1, + ipsec4_logpacketstr(ip, spi))); ipsecstat.in_inval++; goto fail; } +#ifndef PULLDOWN_TEST if (m->m_len < off + sizeof(struct ah) + sizoff + siz1) { m = m_pullup(m, off + sizeof(struct ah) + sizoff + siz1); if (!m) { - printf("IPv4 AH input: can't pullup;" - "dropping the packet for simplicity\n"); + ipseclog((LOG_DEBUG, "IPv4 AH input: can't pullup\n")); ipsecstat.in_inval++; goto fail; } ip = mtod(m, struct ip *); ah = (struct ah *)(((caddr_t)ip) + off); } +#else + IP6_EXTHDR_GET(ah, struct ah *, m, off, + sizeof(struct ah) + sizoff + siz1); + if (ah == NULL) { + ipseclog((LOG_DEBUG, "IPv4 AH input: can't pullup\n")); + ipsecstat.in_inval++; + goto fail; + } +#endif } /* * check for sequence number. */ if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) { if (ipsec_chkreplay(ntohl(((struct newah *)ah)->ah_seq), sav)) ; /*okey*/ else { ipsecstat.in_ahreplay++; - log(LOG_AUTH, "replay packet in IPv4 AH input: %s %s\n", - ipsec4_logpacketstr(ip, spi), - ipsec_logsastr(sav)); + ipseclog((LOG_WARNING, + "replay packet in IPv4 AH input: %s %s\n", + ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav))); goto fail; } } /* * alright, it seems sane. now we are going to check the * cryptographic checksum. */ cksum = malloc(siz1, M_TEMP, M_NOWAIT); if (!cksum) { - printf("IPv4 AH input: couldn't alloc temporary region for cksum\n"); + ipseclog((LOG_DEBUG, "IPv4 AH input: " + "couldn't alloc temporary region for cksum\n")); ipsecstat.in_inval++; goto fail; } - + { +#if 1 /* * some of IP header fields are flipped to the host endian. * convert them back to network endian. VERY stupid. */ ip->ip_len = htons(ip->ip_len + hlen); ip->ip_id = htons(ip->ip_id); ip->ip_off = htons(ip->ip_off); - if (ah4_calccksum(m, (caddr_t)cksum, algo, sav)) { +#endif + if (ah4_calccksum(m, (caddr_t)cksum, siz1, algo, sav)) { free(cksum, M_TEMP); ipsecstat.in_inval++; goto fail; } ipsecstat.in_ahhist[sav->alg_auth]++; +#if 1 /* * flip them back. */ ip->ip_len = ntohs(ip->ip_len) - hlen; ip->ip_id = ntohs(ip->ip_id); ip->ip_off = ntohs(ip->ip_off); +#endif } { caddr_t sumpos = NULL; if (sav->flags & SADB_X_EXT_OLD) { /* RFC 1826 */ sumpos = (caddr_t)(ah + 1); } else { /* RFC 2402 */ sumpos = (caddr_t)(((struct newah *)ah) + 1); } if (bcmp(sumpos, cksum, siz) != 0) { - log(LOG_AUTH, "checksum mismatch in IPv4 AH input: %s %s\n", - ipsec4_logpacketstr(ip, spi), - ipsec_logsastr(sav)); + ipseclog((LOG_WARNING, + "checksum mismatch in IPv4 AH input: %s %s\n", + ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav))); free(cksum, M_TEMP); ipsecstat.in_ahauthfail++; goto fail; } } free(cksum, M_TEMP); m->m_flags |= M_AUTHIPHDR; m->m_flags |= M_AUTHIPDGM; - /* M_AUTH related flags might be cleared here in the future */ +#if 0 + /* + * looks okey, but we need more sanity check. + * XXX should elaborate. + */ + if (ah->ah_nxt == IPPROTO_IPIP || ah->ah_nxt == IPPROTO_IP) { + struct ip *nip; + size_t sizoff; + sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4; + + if (m->m_len < off + sizeof(struct ah) + sizoff + siz1 + hlen) { + m = m_pullup(m, off + sizeof(struct ah) + + sizoff + siz1 + hlen); + if (!m) { + ipseclog((LOG_DEBUG, + "IPv4 AH input: can't pullup\n")); + ipsecstat.in_inval++; + goto fail; + } + } + + nip = (struct ip *)((u_char *)(ah + 1) + sizoff + siz1); + if (nip->ip_src.s_addr != ip->ip_src.s_addr + || nip->ip_dst.s_addr != ip->ip_dst.s_addr) { + m->m_flags &= ~M_AUTHIPHDR; + m->m_flags &= ~M_AUTHIPDGM; + } + } +#ifdef INET6 + else if (ah->ah_nxt == IPPROTO_IPV6) { + m->m_flags &= ~M_AUTHIPHDR; + m->m_flags &= ~M_AUTHIPDGM; + } +#endif /*INET6*/ +#endif /*0*/ + if (m->m_flags & M_AUTHIPHDR && m->m_flags & M_AUTHIPDGM) { +#if 0 + ipseclog((LOG_DEBUG, + "IPv4 AH input: authentication succeess\n")); +#endif ipsecstat.in_ahauthsucc++; } else { - log(LOG_AUTH, "authentication failed in IPv4 AH input: %s %s\n", - ipsec4_logpacketstr(ip, spi), - ipsec_logsastr(sav)); + ipseclog((LOG_WARNING, + "authentication failed in IPv4 AH input: %s %s\n", + ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav))); ipsecstat.in_ahauthfail++; + goto fail; } /* * update sequence number. */ if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) { - (void)ipsec_updatereplay(ntohl(((struct newah *)ah)->ah_seq), sav); + if (ipsec_updatereplay(ntohl(((struct newah *)ah)->ah_seq), sav)) { + ipsecstat.in_ahreplay++; + goto fail; + } } /* was it transmitted over the IPsec tunnel SA? */ if (ipsec4_tunnel_validate(ip, nxt, sav) && nxt == IPPROTO_IPV4) { /* * strip off all the headers that precedes AH. * IP xx AH IP' payload -> IP' payload * * XXX more sanity checks * XXX relationship with gif? */ size_t stripsiz = 0; u_int8_t tos; tos = ip->ip_tos; if (sav->flags & SADB_X_EXT_OLD) { /* RFC 1826 */ stripsiz = sizeof(struct ah) + siz1; } else { /* RFC 2402 */ stripsiz = sizeof(struct newah) + siz1; } m_adj(m, off + stripsiz); if (m->m_len < sizeof(*ip)) { m = m_pullup(m, sizeof(*ip)); if (!m) { ipsecstat.in_inval++; goto fail; } } ip = mtod(m, struct ip *); /* ECN consideration. */ ip_ecn_egress(ip4_ipsec_ecn, &tos, &ip->ip_tos); if (!key_checktunnelsanity(sav, AF_INET, (caddr_t)&ip->ip_src, (caddr_t)&ip->ip_dst)) { - log(LOG_NOTICE, "ipsec tunnel address mismatch in IPv4 AH input: %s %s\n", - ipsec4_logpacketstr(ip, spi), - ipsec_logsastr(sav)); + ipseclog((LOG_NOTICE, "ipsec tunnel address mismatch " + "in IPv4 AH input: %s %s\n", + ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav))); ipsecstat.in_inval++; goto fail; } +#if 0 /* XXX should we call ipfw rather than ipsec_in_reject? */ + /* drop it if it does not match the default policy */ + if (ipsec4_in_reject(m, NULL)) { + ipsecstat.in_polvio++; + goto fail; + } +#endif + +#if 1 /* * Should the inner packet be considered authentic? * My current answer is: NO. * * host1 -- gw1 === gw2 -- host2 * In this case, gw2 can trust the authenticity of the * outer packet, but NOT inner. Packet may be altered * between host1 and gw1. * * host1 -- gw1 === host2 * This case falls into the same scenario as above. * * host1 === host2 * This case is the only case when we may be able to leave * M_AUTHIPHDR and M_AUTHIPDGM set. * However, if host1 is wrongly configured, and allows * attacker to inject some packet with src=host1 and * dst=host2, you are in risk. */ m->m_flags &= ~M_AUTHIPHDR; m->m_flags &= ~M_AUTHIPDGM; +#endif key_sa_recordxfer(sav, m); s = splimp(); if (IF_QFULL(&ipintrq)) { ipsecstat.in_inval++; goto fail; } IF_ENQUEUE(&ipintrq, m); m = NULL; schednetisr(NETISR_IP); /*can be skipped but to make sure*/ splx(s); nxt = IPPROTO_DONE; } else { /* * strip off AH. - * We do deep-copy since KAME requires that - * the packet is placed in a single external mbuf. */ size_t stripsiz = 0; if (sav->flags & SADB_X_EXT_OLD) { /* RFC 1826 */ stripsiz = sizeof(struct ah) + siz1; } else { /* RFC 2402 */ stripsiz = sizeof(struct newah) + siz1; } ip = mtod(m, struct ip *); +#ifndef PULLDOWN_TEST + /* + * We do deep-copy since KAME requires that + * the packet is placed in a single external mbuf. + */ ovbcopy((caddr_t)ip, (caddr_t)(((u_char *)ip) + stripsiz), off); m->m_data += stripsiz; m->m_len -= stripsiz; m->m_pkthdr.len -= stripsiz; +#else + /* + * even in m_pulldown case, we need to strip off AH so that + * we can compute checksum for multiple AH correctly. + */ + if (m->m_len >= stripsiz + off) { + ovbcopy((caddr_t)ip, ((caddr_t)ip) + stripsiz, off); + m->m_data += stripsiz; + m->m_len -= stripsiz; + m->m_pkthdr.len -= stripsiz; + } else { + /* + * this comes with no copy if the boundary is on + * cluster + */ + struct mbuf *n; + n = m_split(m, off, M_DONTWAIT); + if (n == NULL) { + /* m is retained by m_split */ + goto fail; + } + m_adj(n, stripsiz); + m_cat(m, n); + /* m_cat does not update m_pkthdr.len */ + m->m_pkthdr.len += n->m_pkthdr.len; + } +#endif + + if (m->m_len < sizeof(*ip)) { + m = m_pullup(m, sizeof(*ip)); + if (m == NULL) { + ipsecstat.in_inval++; + goto fail; + } + } ip = mtod(m, struct ip *); - /*ip_len is in host endian*/ +#ifdef IPLEN_FLIPPED ip->ip_len = ip->ip_len - stripsiz; +#else + ip->ip_len = htons(ntohs(ip->ip_len) - stripsiz); +#endif ip->ip_p = nxt; /* forget about IP hdr checksum, the check has already been passed */ + key_sa_recordxfer(sav, m); + if (nxt != IPPROTO_DONE) (*inetsw[ip_protox[nxt]].pr_input)(m, off, nxt); else m_freem(m); m = NULL; } if (sav) { KEYDEBUG(KEYDEBUG_IPSEC_STAMP, printf("DP ah4_input call free SA:%p\n", sav)); key_freesav(sav); } ipsecstat.in_success++; return; fail: if (sav) { KEYDEBUG(KEYDEBUG_IPSEC_STAMP, printf("DP ah4_input call free SA:%p\n", sav)); key_freesav(sav); } if (m) m_freem(m); return; } #endif /* INET */ #ifdef INET6 int ah6_input(mp, offp, proto) struct mbuf **mp; int *offp, proto; { struct mbuf *m = *mp; int off = *offp; struct ip6_hdr *ip6; struct ah *ah; u_int32_t spi; struct ah_algorithm *algo; size_t siz; size_t siz1; u_char *cksum; struct secasvar *sav = NULL; u_int16_t nxt; int s; +#ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, off, sizeof(struct ah), IPPROTO_DONE); - + ah = (struct ah *)(mtod(m, caddr_t) + off); +#else + IP6_EXTHDR_GET(ah, struct ah *, m, off, sizeof(struct newah)); + if (ah == NULL) { + ipseclog((LOG_DEBUG, "IPv6 AH input: can't pullup\n")); + ipsecstat.in_inval++; + return IPPROTO_DONE; + } +#endif ip6 = mtod(m, struct ip6_hdr *); - ah = (struct ah *)(((caddr_t)ip6) + off); - nxt = ah->ah_nxt; /* find the sassoc. */ spi = ah->ah_spi; if (ntohs(ip6->ip6_plen) == 0) { - printf("IPv6 AH input: AH with IPv6 jumbogram is not supported.\n"); + ipseclog((LOG_ERR, "IPv6 AH input: " + "AH with IPv6 jumbogram is not supported.\n")); ipsec6stat.in_inval++; goto fail; } if ((sav = key_allocsa(AF_INET6, (caddr_t)&ip6->ip6_src, (caddr_t)&ip6->ip6_dst, IPPROTO_AH, spi)) == 0) { - printf("IPv6 AH input: no key association found for spi %u;" - "dropping the packet for simplicity\n", - (u_int32_t)ntohl(spi)); + ipseclog((LOG_WARNING, + "IPv6 AH input: no key association found for spi %u\n", + (u_int32_t)ntohl(spi))); ipsec6stat.in_nosa++; goto fail; } KEYDEBUG(KEYDEBUG_IPSEC_STAMP, printf("DP ah6_input called to allocate SA:%p\n", sav)); if (sav->state != SADB_SASTATE_MATURE && sav->state != SADB_SASTATE_DYING) { - printf("IPv6 AH input: non-mature/dying SA found for spi %u; " - "dropping the packet for simplicity\n", - (u_int32_t)ntohl(spi)); + ipseclog((LOG_DEBUG, + "IPv6 AH input: non-mature/dying SA found for spi %u; ", + (u_int32_t)ntohl(spi))); ipsec6stat.in_badspi++; goto fail; } if (sav->alg_auth == SADB_AALG_NONE) { - printf("IPv6 AH input: unspecified authentication algorithm " - "for spi %u;" - "dropping the packet for simplicity\n", - (u_int32_t)ntohl(spi)); + ipseclog((LOG_DEBUG, "IPv6 AH input: " + "unspecified authentication algorithm for spi %u\n", + (u_int32_t)ntohl(spi))); ipsec6stat.in_badspi++; goto fail; } algo = &ah_algorithms[sav->alg_auth]; siz = (*algo->sumsiz)(sav); siz1 = ((siz + 3) & ~(4 - 1)); /* * sanity checks for header, 1. */ { int sizoff; sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4; + /* + * Here, we do not do "siz1 == siz". See ah4_input() for complete + * description. + */ + if (siz1 < siz) { + ipseclog((LOG_NOTICE, "sum length too short in IPv6 AH input " + "(%lu, should be at least %lu): %s\n", + (u_long)siz1, (u_long)siz, + ipsec6_logpacketstr(ip6, spi))); + ipsec6stat.in_inval++; + goto fail; + } if ((ah->ah_len << 2) - sizoff != siz1) { - log(LOG_NOTICE, "sum length mismatch in IPv6 AH input " - "(%d should be %u): %s\n", - (ah->ah_len << 2) - sizoff, (unsigned int)siz1, - ipsec6_logpacketstr(ip6, spi)); + ipseclog((LOG_NOTICE, "sum length mismatch in IPv6 AH input " + "(%d should be %lu): %s\n", + (ah->ah_len << 2) - sizoff, (u_long)siz1, + ipsec6_logpacketstr(ip6, spi))); ipsec6stat.in_inval++; goto fail; } +#ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, off, sizeof(struct ah) + sizoff + siz1, IPPROTO_DONE); +#else + IP6_EXTHDR_GET(ah, struct ah *, m, off, + sizeof(struct ah) + sizoff + siz1); + if (ah == NULL) { + ipseclog((LOG_NOTICE, "couldn't pullup gather IPv6 AH checksum part")); + ipsecstat.in_inval++; + m = NULL; + goto fail; + } +#endif } /* * check for sequence number. */ if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) { if (ipsec_chkreplay(ntohl(((struct newah *)ah)->ah_seq), sav)) ; /*okey*/ else { ipsec6stat.in_ahreplay++; - log(LOG_AUTH, "replay packet in IPv6 AH input: %s %s\n", - ipsec6_logpacketstr(ip6, spi), - ipsec_logsastr(sav)); + ipseclog((LOG_WARNING, + "replay packet in IPv6 AH input: %s %s\n", + ipsec6_logpacketstr(ip6, spi), + ipsec_logsastr(sav))); goto fail; } } /* * alright, it seems sane. now we are going to check the * cryptographic checksum. */ cksum = malloc(siz1, M_TEMP, M_NOWAIT); if (!cksum) { - printf("IPv6 AH input: couldn't alloc temporary region for cksum\n"); + ipseclog((LOG_DEBUG, "IPv6 AH input: " + "couldn't alloc temporary region for cksum\n")); ipsec6stat.in_inval++; goto fail; } - - if (ah6_calccksum(m, (caddr_t)cksum, algo, sav)) { + + if (ah6_calccksum(m, (caddr_t)cksum, siz1, algo, sav)) { free(cksum, M_TEMP); ipsec6stat.in_inval++; goto fail; } ipsec6stat.in_ahhist[sav->alg_auth]++; { caddr_t sumpos = NULL; if (sav->flags & SADB_X_EXT_OLD) { /* RFC 1826 */ sumpos = (caddr_t)(ah + 1); } else { /* RFC 2402 */ sumpos = (caddr_t)(((struct newah *)ah) + 1); } if (bcmp(sumpos, cksum, siz) != 0) { - log(LOG_AUTH, "checksum mismatch in IPv6 AH input: %s %s\n", - ipsec6_logpacketstr(ip6, spi), - ipsec_logsastr(sav)); + ipseclog((LOG_WARNING, + "checksum mismatch in IPv6 AH input: %s %s\n", + ipsec6_logpacketstr(ip6, spi), ipsec_logsastr(sav))); free(cksum, M_TEMP); ipsec6stat.in_ahauthfail++; goto fail; } } free(cksum, M_TEMP); m->m_flags |= M_AUTHIPHDR; m->m_flags |= M_AUTHIPDGM; - /* M_AUTH related flags might be cleared here in the future */ +#if 0 + /* + * looks okey, but we need more sanity check. + * XXX should elaborate. + */ + if (ah->ah_nxt == IPPROTO_IPV6) { + struct ip6_hdr *nip6; + size_t sizoff; + sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4; + + IP6_EXTHDR_CHECK(m, off, sizeof(struct ah) + sizoff + siz1 + + sizeof(struct ip6_hdr), IPPROTO_DONE); + + nip6 = (struct ip6_hdr *)((u_char *)(ah + 1) + sizoff + siz1); + if (!IN6_ARE_ADDR_EQUAL(&nip6->ip6_src, &ip6->ip6_src) + || !IN6_ARE_ADDR_EQUAL(&nip6->ip6_dst, &ip6->ip6_dst)) { + m->m_flags &= ~M_AUTHIPHDR; + m->m_flags &= ~M_AUTHIPDGM; + } + } else if (ah->ah_nxt == IPPROTO_IPIP) { + m->m_flags &= ~M_AUTHIPHDR; + m->m_flags &= ~M_AUTHIPDGM; + } else if (ah->ah_nxt == IPPROTO_IP) { + m->m_flags &= ~M_AUTHIPHDR; + m->m_flags &= ~M_AUTHIPDGM; + } +#endif + if (m->m_flags & M_AUTHIPHDR && m->m_flags & M_AUTHIPDGM) { +#if 0 + ipseclog((LOG_DEBUG, + "IPv6 AH input: authentication succeess\n")); +#endif ipsec6stat.in_ahauthsucc++; } else { - log(LOG_AUTH, "authentication failed in IPv6 AH input: %s %s\n", - ipsec6_logpacketstr(ip6, spi), - ipsec_logsastr(sav)); + ipseclog((LOG_WARNING, + "authentication failed in IPv6 AH input: %s %s\n", + ipsec6_logpacketstr(ip6, spi), ipsec_logsastr(sav))); ipsec6stat.in_ahauthfail++; + goto fail; } /* * update sequence number. */ if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) { - (void)ipsec_updatereplay(ntohl(((struct newah *)ah)->ah_seq), sav); + if (ipsec_updatereplay(ntohl(((struct newah *)ah)->ah_seq), sav)) { + ipsec6stat.in_ahreplay++; + goto fail; + } } /* was it transmitted over the IPsec tunnel SA? */ if (ipsec6_tunnel_validate(ip6, nxt, sav) && nxt == IPPROTO_IPV6) { /* * strip off all the headers that precedes AH. * IP6 xx AH IP6' payload -> IP6' payload * * XXX more sanity checks * XXX relationship with gif? */ size_t stripsiz = 0; u_int32_t flowinfo; /*net endian*/ flowinfo = ip6->ip6_flow; if (sav->flags & SADB_X_EXT_OLD) { /* RFC 1826 */ stripsiz = sizeof(struct ah) + siz1; } else { /* RFC 2402 */ stripsiz = sizeof(struct newah) + siz1; } m_adj(m, off + stripsiz); if (m->m_len < sizeof(*ip6)) { /* * m_pullup is prohibited in KAME IPv6 input processing * but there's no other way! */ m = m_pullup(m, sizeof(*ip6)); if (!m) { ipsec6stat.in_inval++; goto fail; } } ip6 = mtod(m, struct ip6_hdr *); /* ECN consideration. */ ip6_ecn_egress(ip6_ipsec_ecn, &flowinfo, &ip6->ip6_flow); if (!key_checktunnelsanity(sav, AF_INET6, (caddr_t)&ip6->ip6_src, (caddr_t)&ip6->ip6_dst)) { - log(LOG_NOTICE, "ipsec tunnel address mismatch in IPv6 AH input: %s %s\n", - ipsec6_logpacketstr(ip6, spi), - ipsec_logsastr(sav)); + ipseclog((LOG_NOTICE, "ipsec tunnel address mismatch " + "in IPv6 AH input: %s %s\n", + ipsec6_logpacketstr(ip6, spi), + ipsec_logsastr(sav))); ipsec6stat.in_inval++; goto fail; } +#if 0 /* XXX should we call ipfw rather than ipsec_in_reject? */ + /* drop it if it does not match the default policy */ + if (ipsec6_in_reject(m, NULL)) { + ipsec6stat.in_polvio++; + goto fail; + } +#endif + +#if 1 /* * should the inner packet be considered authentic? * see comment in ah4_input(). */ m->m_flags &= ~M_AUTHIPHDR; m->m_flags &= ~M_AUTHIPDGM; +#endif key_sa_recordxfer(sav, m); s = splimp(); if (IF_QFULL(&ip6intrq)) { ipsec6stat.in_inval++; goto fail; } IF_ENQUEUE(&ip6intrq, m); m = NULL; schednetisr(NETISR_IPV6); /*can be skipped but to make sure*/ splx(s); nxt = IPPROTO_DONE; } else { /* * strip off AH. - * We do deep-copy since KAME requires that - * the packet is placed in a single mbuf. */ size_t stripsiz = 0; char *prvnxtp; /* * Copy the value of the next header field of AH to the * next header field of the previous header. * This is necessary because AH will be stripped off below. */ prvnxtp = ip6_get_prevhdr(m, off); /* XXX */ *prvnxtp = nxt; if (sav->flags & SADB_X_EXT_OLD) { /* RFC 1826 */ stripsiz = sizeof(struct ah) + siz1; } else { /* RFC 2402 */ stripsiz = sizeof(struct newah) + siz1; } ip6 = mtod(m, struct ip6_hdr *); - ovbcopy((caddr_t)ip6, (caddr_t)(((u_char *)ip6) + stripsiz), - off); +#ifndef PULLDOWN_TEST + /* + * We do deep-copy since KAME requires that + * the packet is placed in a single mbuf. + */ + ovbcopy((caddr_t)ip6, ((caddr_t)ip6) + stripsiz, off); m->m_data += stripsiz; m->m_len -= stripsiz; m->m_pkthdr.len -= stripsiz; +#else + /* + * even in m_pulldown case, we need to strip off AH so that + * we can compute checksum for multiple AH correctly. + */ + if (m->m_len >= stripsiz + off) { + ovbcopy((caddr_t)ip6, ((caddr_t)ip6) + stripsiz, off); + m->m_data += stripsiz; + m->m_len -= stripsiz; + m->m_pkthdr.len -= stripsiz; + } else { + /* + * this comes with no copy if the boundary is on + * cluster + */ + struct mbuf *n; + n = m_split(m, off, M_DONTWAIT); + if (n == NULL) { + /* m is retained by m_split */ + goto fail; + } + m_adj(n, stripsiz); + m_cat(m, n); + /* m_cat does not update m_pkthdr.len */ + m->m_pkthdr.len += n->m_pkthdr.len; + } +#endif ip6 = mtod(m, struct ip6_hdr *); + /* XXX jumbogram */ ip6->ip6_plen = htons(ntohs(ip6->ip6_plen) - stripsiz); key_sa_recordxfer(sav, m); } *offp = off; *mp = m; if (sav) { KEYDEBUG(KEYDEBUG_IPSEC_STAMP, printf("DP ah6_input call free SA:%p\n", sav)); key_freesav(sav); } ipsec6stat.in_success++; return nxt; fail: if (sav) { KEYDEBUG(KEYDEBUG_IPSEC_STAMP, printf("DP ah6_input call free SA:%p\n", sav)); key_freesav(sav); } if (m) m_freem(m); return IPPROTO_DONE; } #endif /* INET6 */ Index: head/sys/netinet6/ah_output.c =================================================================== --- head/sys/netinet6/ah_output.c (revision 62586) +++ head/sys/netinet6/ah_output.c (revision 62587) @@ -1,530 +1,557 @@ +/* $FreeBSD$ */ +/* $KAME: ah_output.c,v 1.22 2000/07/03 13:23:28 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* * RFC1826/2402 authentication header. */ +#include "opt_inet.h" #include "opt_inet6.h" -#include "opt_ipsec.h" -#define ahdprintf(x) printf x - #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #ifdef INET6 -#include +#include #include -#include +#include #endif #include -#include #ifdef INET6 #include +#endif +#include +#ifdef INET6 #include #endif #include #include -#ifdef IPSEC_DEBUG -#include -#else -#define KEYDEBUG(lev,arg) -#endif #include static struct in_addr *ah4_finaldst __P((struct mbuf *)); /* * compute AH header size. * transport mode only. for tunnel mode, we should implement * virtual interface, and control MTU/MSS by the interface MTU. */ size_t ah_hdrsiz(isr) struct ipsecrequest *isr; { struct ah_algorithm *algo; size_t hdrsiz; /* sanity check */ if (isr == NULL) panic("ah_hdrsiz: NULL was passed.\n"); if (isr->saidx.proto != IPPROTO_AH) panic("unsupported mode passed to ah_hdrsiz"); if (isr->sav == NULL) - goto contrive; + goto estimate; if (isr->sav->state != SADB_SASTATE_MATURE && isr->sav->state != SADB_SASTATE_DYING) - goto contrive; + goto estimate; /* we need transport mode AH. */ algo = &ah_algorithms[isr->sav->alg_auth]; if (!algo) - goto contrive; + goto estimate; /* * XXX * right now we don't calcurate the padding size. simply * treat the padding size as constant, for simplicity. * * XXX variable size padding support */ hdrsiz = (((*algo->sumsiz)(isr->sav) + 3) & ~(4 - 1)); if (isr->sav->flags & SADB_X_EXT_OLD) hdrsiz += sizeof(struct ah); else hdrsiz += sizeof(struct newah); return hdrsiz; - contrive: + estimate: /* ASSUMING: * sizeof(struct newah) > sizeof(struct ah). * 16 = (16 + 3) & ~(4 - 1). */ return sizeof(struct newah) + 16; } /* * Modify the packet so that it includes the authentication data. * The mbuf passed must start with IPv4 header. * * assumes that the first mbuf contains IPv4 header + option only. * the function does not modify m. */ int ah4_output(m, isr) struct mbuf *m; struct ipsecrequest *isr; { struct secasvar *sav = isr->sav; struct ah_algorithm *algo; u_int32_t spi; u_char *ahdrpos; u_char *ahsumpos = NULL; size_t hlen = 0; /*IP header+option in bytes*/ size_t plen = 0; /*AH payload size in bytes*/ size_t ahlen = 0; /*plen + sizeof(ah)*/ struct ip *ip; struct in_addr dst; struct in_addr *finaldst; int error; /* sanity checks */ if ((sav->flags & SADB_X_EXT_OLD) == 0 && !sav->replay) { struct ip *ip; ip = mtod(m, struct ip *); - printf("ah4_output: internal error: " - "sav->replay is null: " - "%x->%x, SPI=%u\n", + ipseclog((LOG_DEBUG, "ah4_output: internal error: " + "sav->replay is null: %x->%x, SPI=%u\n", (u_int32_t)ntohl(ip->ip_src.s_addr), (u_int32_t)ntohl(ip->ip_dst.s_addr), - (u_int32_t)ntohl(sav->spi)); + (u_int32_t)ntohl(sav->spi))); ipsecstat.out_inval++; m_freem(m); return EINVAL; } algo = &ah_algorithms[sav->alg_auth]; spi = sav->spi; /* * determine the size to grow. */ if (sav->flags & SADB_X_EXT_OLD) { /* RFC 1826 */ plen = ((*algo->sumsiz)(sav) + 3) & ~(4 - 1); /*XXX pad to 8byte?*/ ahlen = plen + sizeof(struct ah); } else { /* RFC 2402 */ plen = ((*algo->sumsiz)(sav) + 3) & ~(4 - 1); /*XXX pad to 8byte?*/ ahlen = plen + sizeof(struct newah); } /* * grow the mbuf to accomodate AH. */ ip = mtod(m, struct ip *); #ifdef _IP_VHL hlen = IP_VHL_HL(ip->ip_vhl) << 2; #else hlen = ip->ip_hl << 2; #endif if (m->m_len != hlen) panic("ah4_output: assumption failed (first mbuf length)"); if (M_LEADINGSPACE(m->m_next) < ahlen) { struct mbuf *n; MGET(n, M_DONTWAIT, MT_DATA); if (!n) { - printf("ENOBUFS in ah4_output %d\n", __LINE__); + ipseclog((LOG_DEBUG, "ENOBUFS in ah4_output %d\n", + __LINE__)); m_freem(m); return ENOBUFS; } n->m_len = ahlen; n->m_next = m->m_next; m->m_next = n; m->m_pkthdr.len += ahlen; ahdrpos = mtod(n, u_char *); } else { m->m_next->m_len += ahlen; m->m_next->m_data -= ahlen; m->m_pkthdr.len += ahlen; ahdrpos = mtod(m->m_next, u_char *); } ip = mtod(m, struct ip *); /*just to be sure*/ /* * initialize AH. */ if (sav->flags & SADB_X_EXT_OLD) { struct ah *ahdr; ahdr = (struct ah *)ahdrpos; ahsumpos = (u_char *)(ahdr + 1); ahdr->ah_len = plen >> 2; ahdr->ah_nxt = ip->ip_p; ahdr->ah_reserve = htons(0); ahdr->ah_spi = spi; bzero(ahdr + 1, plen); } else { struct newah *ahdr; ahdr = (struct newah *)ahdrpos; ahsumpos = (u_char *)(ahdr + 1); ahdr->ah_len = (plen >> 2) + 1; /* plus one for seq# */ ahdr->ah_nxt = ip->ip_p; ahdr->ah_reserve = htons(0); ahdr->ah_spi = spi; + if (sav->replay->count == ~0) { + if ((sav->flags & SADB_X_EXT_CYCSEQ) == 0) { + /* XXX Is it noisy ? */ + ipseclog((LOG_WARNING, + "replay counter overflowed. %s\n", + ipsec_logsastr(sav))); + ipsecstat.out_inval++; + m_freem(m); + return EINVAL; + } + } sav->replay->count++; /* * XXX sequence number must not be cycled, if the SA is * installed by IKE daemon. */ ahdr->ah_seq = htonl(sav->replay->count); bzero(ahdr + 1, plen); } /* * modify IPv4 header. */ ip->ip_p = IPPROTO_AH; if (ahlen < (IP_MAXPACKET - ntohs(ip->ip_len))) ip->ip_len = htons(ntohs(ip->ip_len) + ahlen); else { - printf("IPv4 AH output: size exceeds limit\n"); + ipseclog((LOG_ERR, "IPv4 AH output: size exceeds limit\n")); ipsecstat.out_inval++; m_freem(m); return EMSGSIZE; } /* * If there is source routing option, update destination field in * the IPv4 header to the final destination. * Note that we do not need to update source routing option itself * (as done in IPv4 AH processing -- see ip6_output()), since * source routing option is not part of the ICV computation. */ finaldst = ah4_finaldst(m); if (finaldst) { dst.s_addr = ip->ip_dst.s_addr; ip->ip_dst.s_addr = finaldst->s_addr; } /* * calcurate the checksum, based on security association * and the algorithm specified. */ - error = ah4_calccksum(m, (caddr_t)ahsumpos, algo, sav); + error = ah4_calccksum(m, (caddr_t)ahsumpos, plen, algo, sav); if (error) { - printf("error after ah4_calccksum, called from ah4_output"); + ipseclog((LOG_ERR, + "error after ah4_calccksum, called from ah4_output")); + m_freem(m); m = NULL; ipsecstat.out_inval++; return error; } if (finaldst) { ip = mtod(m, struct ip *); /*just to make sure*/ ip->ip_dst.s_addr = dst.s_addr; } ipsecstat.out_success++; ipsecstat.out_ahhist[sav->alg_auth]++; key_sa_recordxfer(sav, m); return 0; } /* Calculate AH length */ int ah_hdrlen(sav) struct secasvar *sav; { struct ah_algorithm *algo; int plen, ahlen; - + algo = &ah_algorithms[sav->alg_auth]; if (sav->flags & SADB_X_EXT_OLD) { /* RFC 1826 */ plen = ((*algo->sumsiz)(sav) + 3) & ~(4 - 1); /*XXX pad to 8byte?*/ ahlen = plen + sizeof(struct ah); } else { /* RFC 2402 */ plen = ((*algo->sumsiz)(sav) + 3) & ~(4 - 1); /*XXX pad to 8byte?*/ ahlen = plen + sizeof(struct newah); } return(ahlen); } #ifdef INET6 /* * Fill in the Authentication Header and calculate checksum. */ int ah6_output(m, nexthdrp, md, isr) struct mbuf *m; u_char *nexthdrp; struct mbuf *md; struct ipsecrequest *isr; { struct mbuf *mprev; struct mbuf *mah; struct secasvar *sav = isr->sav; struct ah_algorithm *algo; u_int32_t spi; u_char *ahsumpos = NULL; size_t plen; /*AH payload size in bytes*/ int error = 0; int ahlen; struct ip6_hdr *ip6; if (m->m_len < sizeof(struct ip6_hdr)) { - printf("ah6_output: first mbuf too short\n"); + ipseclog((LOG_DEBUG, "ah6_output: first mbuf too short\n")); m_freem(m); return EINVAL; } ahlen = ah_hdrlen(sav); if (ahlen == 0) return 0; for (mprev = m; mprev && mprev->m_next != md; mprev = mprev->m_next) ; if (!mprev || mprev->m_next != md) { - printf("ah6_output: md is not in chain\n"); + ipseclog((LOG_DEBUG, "ah6_output: md is not in chain\n")); m_freem(m); return EINVAL; } MGET(mah, M_DONTWAIT, MT_DATA); if (!mah) { m_freem(m); return ENOBUFS; } if (ahlen > MLEN) { MCLGET(mah, M_DONTWAIT); if ((mah->m_flags & M_EXT) == 0) { m_free(mah); m_freem(m); return ENOBUFS; } } mah->m_len = ahlen; mah->m_next = md; mprev->m_next = mah; m->m_pkthdr.len += ahlen; /* fix plen */ if (m->m_pkthdr.len - sizeof(struct ip6_hdr) > IPV6_MAXPACKET) { - printf("ip6_output: AH with IPv6 jumbogram is not supported\n"); + ipseclog((LOG_ERR, + "ip6_output: AH with IPv6 jumbogram is not supported\n")); m_freem(m); return EINVAL; } ip6 = mtod(m, struct ip6_hdr *); ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(struct ip6_hdr)); if ((sav->flags & SADB_X_EXT_OLD) == 0 && !sav->replay) { - printf("ah6_output: internal error: " + ipseclog((LOG_DEBUG, "ah6_output: internal error: " "sav->replay is null: SPI=%u\n", - (u_int32_t)ntohl(sav->spi)); + (u_int32_t)ntohl(sav->spi))); ipsec6stat.out_inval++; - return 0; /* no change at all */ + m_freem(m); + return EINVAL; } algo = &ah_algorithms[sav->alg_auth]; spi = sav->spi; /* * initialize AH. */ if (sav->flags & SADB_X_EXT_OLD) { struct ah *ahdr = mtod(mah, struct ah *); plen = mah->m_len - sizeof(struct ah); ahsumpos = (u_char *)(ahdr + 1); ahdr->ah_nxt = *nexthdrp; *nexthdrp = IPPROTO_AH; ahdr->ah_len = plen >> 2; ahdr->ah_reserve = htons(0); ahdr->ah_spi = spi; bzero(ahdr + 1, plen); } else { struct newah *ahdr = mtod(mah, struct newah *); plen = mah->m_len - sizeof(struct newah); ahsumpos = (u_char *)(ahdr + 1); ahdr->ah_nxt = *nexthdrp; *nexthdrp = IPPROTO_AH; ahdr->ah_len = (plen >> 2) + 1; /* plus one for seq# */ ahdr->ah_reserve = htons(0); ahdr->ah_spi = spi; + if (sav->replay->count == ~0) { + if ((sav->flags & SADB_X_EXT_CYCSEQ) == 0) { + /* XXX Is it noisy ? */ + ipseclog((LOG_WARNING, + "replay counter overflowed. %s\n", + ipsec_logsastr(sav))); + ipsecstat.out_inval++; + m_freem(m); + return EINVAL; + } + } sav->replay->count++; /* * XXX sequence number must not be cycled, if the SA is * installed by IKE daemon. */ ahdr->ah_seq = htonl(sav->replay->count); bzero(ahdr + 1, plen); } /* * calcurate the checksum, based on security association * and the algorithm specified. */ - error = ah6_calccksum(m, (caddr_t)ahsumpos, algo, sav); - if (error) + error = ah6_calccksum(m, (caddr_t)ahsumpos, plen, algo, sav); + if (error) { ipsec6stat.out_inval++; - else + m_freem(m); + } else { ipsec6stat.out_success++; + key_sa_recordxfer(sav, m); + } ipsec6stat.out_ahhist[sav->alg_auth]++; - key_sa_recordxfer(sav, m); return(error); } #endif /* * Find the final destination if there is loose/strict source routing option. * Returns NULL if there's no source routing options. * Returns NULL on errors too. * Note that this function will return a pointer INTO the given parameter, * struct mbuf *m. * The mbuf must be pulled up toward, at least, ip option part. */ static struct in_addr * ah4_finaldst(m) struct mbuf *m; { struct ip *ip; int optlen; u_char *q; int i; int hlen; if (!m) panic("ah4_finaldst: m == NULL"); ip = mtod(m, struct ip *); hlen = (ip->ip_hl << 2); if (m->m_len < hlen) { - printf("ah4_finaldst: parameter mbuf wrong (not pulled up)\n"); + ipseclog((LOG_DEBUG, + "ah4_finaldst: parameter mbuf wrong (not pulled up)\n")); return NULL; } if (hlen == sizeof(struct ip)) return NULL; optlen = hlen - sizeof(struct ip); if (optlen < 0) { - printf("ah4_finaldst: wrong optlen %d\n", optlen); + ipseclog((LOG_DEBUG, "ah4_finaldst: wrong optlen %d\n", + optlen)); return NULL; } q = (u_char *)(ip + 1); i = 0; while (i < optlen) { switch (q[i + IPOPT_OPTVAL]) { case IPOPT_EOL: i = optlen; /* bye */ break; case IPOPT_NOP: i++; break; case IPOPT_LSRR: case IPOPT_SSRR: if (q[i + IPOPT_OLEN] <= 0 || optlen - i < q[i + IPOPT_OLEN]) { - printf("ip_finaldst: invalid IP option " - "(code=%02x len=%02x)\n", - q[i + IPOPT_OPTVAL], q[i + IPOPT_OLEN]); + ipseclog((LOG_ERR, + "ip_finaldst: invalid IP option " + "(code=%02x len=%02x)\n", + q[i + IPOPT_OPTVAL], q[i + IPOPT_OLEN])); return NULL; } i += q[i + IPOPT_OLEN] - sizeof(struct in_addr); return (struct in_addr *)(q + i); default: if (q[i + IPOPT_OLEN] <= 0 || optlen - i < q[i + IPOPT_OLEN]) { - printf("ip_finaldst: invalid IP option " - "(code=%02x len=%02x)\n", - q[i + IPOPT_OPTVAL], q[i + IPOPT_OLEN]); + ipseclog((LOG_ERR, + "ip_finaldst: invalid IP option " + "(code=%02x len=%02x)\n", + q[i + IPOPT_OPTVAL], q[i + IPOPT_OLEN])); return NULL; } i += q[i + IPOPT_OLEN]; break; } } return NULL; } Index: head/sys/netinet6/dest6.c =================================================================== --- head/sys/netinet6/dest6.c (revision 62586) +++ head/sys/netinet6/dest6.c (revision 62587) @@ -1,106 +1,122 @@ +/* $FreeBSD$ */ +/* $KAME: dest6.c,v 1.12 2000/05/05 11:00:57 sumikawa Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ +#include "opt_inet.h" +#include "opt_inet6.h" + #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include -#include +#include /* * Destination options header processing. */ int dest6_input(mp, offp, proto) struct mbuf **mp; int *offp, proto; { register struct mbuf *m = *mp; int off = *offp, dstoptlen, optlen; struct ip6_dest *dstopts; u_int8_t *opt; /* validation of the length of the header */ +#ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, off, sizeof(*dstopts), IPPROTO_DONE); dstopts = (struct ip6_dest *)(mtod(m, caddr_t) + off); +#else + IP6_EXTHDR_GET(dstopts, struct ip6_dest *, m, off, sizeof(*dstopts)); + if (dstopts == NULL) + return IPPROTO_DONE; +#endif dstoptlen = (dstopts->ip6d_len + 1) << 3; +#ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, off, dstoptlen, IPPROTO_DONE); dstopts = (struct ip6_dest *)(mtod(m, caddr_t) + off); +#else + IP6_EXTHDR_GET(dstopts, struct ip6_dest *, m, off, dstoptlen); + if (dstopts == NULL) + return IPPROTO_DONE; +#endif off += dstoptlen; dstoptlen -= sizeof(struct ip6_dest); opt = (u_int8_t *)dstopts + sizeof(struct ip6_dest); /* search header for all options. */ for (optlen = 0; dstoptlen > 0; dstoptlen -= optlen, opt += optlen) { switch(*opt) { case IP6OPT_PAD1: optlen = 1; break; case IP6OPT_PADN: if (dstoptlen < IP6OPT_MINLEN) { ip6stat.ip6s_toosmall++; goto bad; } optlen = *(opt + 1) + 2; break; default: /* unknown option */ if (dstoptlen < IP6OPT_MINLEN) { ip6stat.ip6s_toosmall++; goto bad; } if ((optlen = ip6_unknown_opt(opt, m, opt-mtod(m, u_int8_t *))) == -1) return(IPPROTO_DONE); optlen += 2; break; } } *offp = off; return(dstopts->ip6d_nxt); bad: m_freem(m); return(IPPROTO_DONE); } Index: head/sys/netinet6/esp.h =================================================================== --- head/sys/netinet6/esp.h (revision 62586) +++ head/sys/netinet6/esp.h (revision 62587) @@ -1,99 +1,101 @@ +/* $FreeBSD$ */ +/* $KAME: esp.h,v 1.8 2000/07/02 13:23:33 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* * RFC1827/2406 Encapsulated Security Payload. */ #ifndef _NETINET6_ESP_H_ -#define _NETINET6_ESP_H_ +#define _NETINET6_ESP_H_ struct secasvar; struct esp { u_int32_t esp_spi; /* ESP */ /*variable size, 32bit bound*/ /* Initialization Vector */ /*variable size*/ /* Payload data */ /*variable size*/ /* padding */ /*8bit*/ /* pad size */ /*8bit*/ /* next header */ /*8bit*/ /* next header */ /*variable size, 32bit bound*/ /* Authentication data (new IPsec) */ }; struct newesp { u_int32_t esp_spi; /* ESP */ u_int32_t esp_seq; /* Sequence number */ /*variable size*/ /* (IV and) Payload data */ /*variable size*/ /* padding */ /*8bit*/ /* pad size */ /*8bit*/ /* next header */ /*8bit*/ /* next header */ /*variable size, 32bit bound*/ /* Authentication data */ }; struct esptail { u_int8_t esp_padlen; /* pad length */ u_int8_t esp_nxt; /* Next header */ /*variable size, 32bit bound*/ /* Authentication data (new IPsec)*/ }; struct esp_algorithm_state { struct secasvar *sav; void* foo; /*per algorithm data - maybe*/ }; /* XXX yet to be defined */ struct esp_algorithm { size_t padbound; /* pad boundary, in byte */ int (*mature) __P((struct secasvar *)); int keymin; /* in bits */ int keymax; /* in bits */ + const char *name; int (*ivlen) __P((struct secasvar *)); int (*decrypt) __P((struct mbuf *, size_t, struct secasvar *, struct esp_algorithm *, int)); int (*encrypt) __P((struct mbuf *, size_t, size_t, struct secasvar *, struct esp_algorithm *, int)); }; #ifdef _KERNEL extern struct esp_algorithm esp_algorithms[]; /* crypt routines */ extern int esp4_output __P((struct mbuf *, struct ipsecrequest *)); -extern void esp4_input __P((struct mbuf *, int, int)); +extern void esp4_input __P((struct mbuf *, ...)); extern size_t esp_hdrsiz __P((struct ipsecrequest *)); -#endif +#endif /*_KERNEL*/ extern int esp_auth __P((struct mbuf *, size_t, size_t, struct secasvar *, u_char *)); #endif /*_NETINET6_ESP_H_*/ Index: head/sys/netinet6/esp6.h =================================================================== --- head/sys/netinet6/esp6.h (revision 62586) +++ head/sys/netinet6/esp6.h (revision 62587) @@ -1,45 +1,46 @@ +/* $FreeBSD$ */ +/* $KAME: esp.h,v 1.8 2000/07/02 13:23:33 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* * RFC1827/2406 Encapsulated Security Payload. */ #ifndef _NETINET6_ESP6_H_ -#define _NETINET6_ESP6_H_ +#define _NETINET6_ESP6_H_ #ifdef _KERNEL extern int esp6_output __P((struct mbuf *, u_char *, struct mbuf *, struct ipsecrequest *)); extern int esp6_input __P((struct mbuf **, int *, int)); -#endif +#endif /*_KERNEL*/ #endif /*_NETINET6_ESP6_H_*/ Index: head/sys/netinet6/esp_core.c =================================================================== --- head/sys/netinet6/esp_core.c (revision 62586) +++ head/sys/netinet6/esp_core.c (revision 62587) @@ -1,1234 +1,1292 @@ +/* $FreeBSD$ */ +/* $KAME: esp_core.c,v 1.15 2000/06/14 10:41:18 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ +#include "opt_inet.h" #include "opt_inet6.h" -#include "opt_ipsec.h" #include #include #include #include #include #include #include #include +#include #include #include #include #include #ifdef INET6 -#include +#include #include -#include +#include #endif #include -#include #ifdef INET6 #include +#endif +#include +#ifdef INET6 #include #endif -#ifdef IPSEC_ESP #include #ifdef INET6 #include #endif -#endif #include -#include #include #include #include #include #include #include static int esp_null_mature __P((struct secasvar *)); static int esp_null_ivlen __P((struct secasvar *)); static int esp_null_decrypt __P((struct mbuf *, size_t, struct secasvar *, struct esp_algorithm *, int)); static int esp_null_encrypt __P((struct mbuf *, size_t, size_t, struct secasvar *, struct esp_algorithm *, int)); static int esp_descbc_mature __P((struct secasvar *)); static int esp_descbc_ivlen __P((struct secasvar *)); static int esp_descbc_decrypt __P((struct mbuf *, size_t, struct secasvar *, struct esp_algorithm *, int)); static int esp_descbc_encrypt __P((struct mbuf *, size_t, size_t, struct secasvar *, struct esp_algorithm *, int)); static int esp_cbc_mature __P((struct secasvar *)); static int esp_blowfish_cbc_decrypt __P((struct mbuf *, size_t, struct secasvar *, struct esp_algorithm *, int)); static int esp_blowfish_cbc_encrypt __P((struct mbuf *, size_t, size_t, struct secasvar *, struct esp_algorithm *, int)); static int esp_blowfish_cbc_ivlen __P((struct secasvar *)); static int esp_cast128cbc_ivlen __P((struct secasvar *)); static int esp_cast128cbc_decrypt __P((struct mbuf *, size_t, struct secasvar *, struct esp_algorithm *, int)); static int esp_cast128cbc_encrypt __P((struct mbuf *, size_t, size_t, struct secasvar *, struct esp_algorithm *, int)); static int esp_3descbc_ivlen __P((struct secasvar *)); static int esp_3descbc_decrypt __P((struct mbuf *, size_t, struct secasvar *, struct esp_algorithm *, int)); static int esp_3descbc_encrypt __P((struct mbuf *, size_t, size_t, struct secasvar *, struct esp_algorithm *, int)); static int esp_rc5cbc_ivlen __P((struct secasvar *)); static int esp_rc5cbc_decrypt __P((struct mbuf *, size_t, struct secasvar *, struct esp_algorithm *, int)); static int esp_rc5cbc_encrypt __P((struct mbuf *, size_t, size_t, struct secasvar *, struct esp_algorithm *, int)); static void esp_increment_iv __P((struct secasvar *)); static caddr_t mbuf_find_offset __P((struct mbuf *, size_t, size_t)); /* NOTE: The order depends on SADB_EALG_x in netkey/keyv2.h */ struct esp_algorithm esp_algorithms[] = { { 0, 0, 0, 0, 0, 0, 0, }, - { 8, esp_descbc_mature, 64, 64, + { 8, esp_descbc_mature, 64, 64, "des-cbc", esp_descbc_ivlen, esp_descbc_decrypt, esp_descbc_encrypt, }, - { 8, esp_cbc_mature, 192, 192, + { 8, esp_cbc_mature, 192, 192, "3des-cbc", esp_3descbc_ivlen, esp_3descbc_decrypt, esp_3descbc_encrypt, }, - { 1, esp_null_mature, 0, 2048, + { 1, esp_null_mature, 0, 2048, "null", esp_null_ivlen, esp_null_decrypt, esp_null_encrypt, }, - { 8, esp_cbc_mature, 40, 448, + { 8, esp_cbc_mature, 40, 448, "blowfish-cbc", esp_blowfish_cbc_ivlen, esp_blowfish_cbc_decrypt, esp_blowfish_cbc_encrypt, }, - { 8, esp_cbc_mature, 40, 128, + { 8, esp_cbc_mature, 40, 128, "cast128-cbc", esp_cast128cbc_ivlen, esp_cast128cbc_decrypt, esp_cast128cbc_encrypt, }, - { 8, esp_cbc_mature, 40, 2040, + { 8, esp_cbc_mature, 40, 2040, "rc5-cbc", esp_rc5cbc_ivlen, esp_rc5cbc_decrypt, esp_rc5cbc_encrypt, }, }; /* * mbuf assumption: foo_encrypt() assumes that IV part is placed in a single * mbuf, not across multiple mbufs. */ static int esp_null_mature(sav) struct secasvar *sav; { /* anything is okay */ return 0; } static int esp_null_ivlen(sav) struct secasvar *sav; { return 0; } static int esp_null_decrypt(m, off, sav, algo, ivlen) struct mbuf *m; size_t off; /* offset to ESP header */ struct secasvar *sav; struct esp_algorithm *algo; int ivlen; { return 0; /* do nothing */ } static int esp_null_encrypt(m, off, plen, sav, algo, ivlen) struct mbuf *m; size_t off; /* offset to ESP header */ size_t plen; /* payload length (to be encrypted) */ struct secasvar *sav; struct esp_algorithm *algo; int ivlen; { return 0; /* do nothing */ } static int esp_descbc_mature(sav) struct secasvar *sav; { struct esp_algorithm *algo; if (!(sav->flags & SADB_X_EXT_OLD) && (sav->flags & SADB_X_EXT_IV4B)) { - printf("esp_cbc_mature: algorithm incompatible with 4 octets IV length\n"); + ipseclog((LOG_ERR, "esp_cbc_mature: " + "algorithm incompatible with 4 octets IV length\n")); return 1; } if (!sav->key_enc) { - printf("esp_descbc_mature: no key is given.\n"); + ipseclog((LOG_ERR, "esp_descbc_mature: no key is given.\n")); return 1; } algo = &esp_algorithms[sav->alg_enc]; if (_KEYBITS(sav->key_enc) < algo->keymin || algo->keymax < _KEYBITS(sav->key_enc)) { - printf("esp_descbc_mature: invalid key length %d.\n", - _KEYBITS(sav->key_enc)); + ipseclog((LOG_ERR, + "esp_descbc_mature: invalid key length %d.\n", + _KEYBITS(sav->key_enc))); return 1; } /* weak key check */ if (des_is_weak_key((C_Block *)_KEYBUF(sav->key_enc))) { - printf("esp_descbc_mature: weak key was passed.\n"); + ipseclog((LOG_ERR, + "esp_descbc_mature: weak key was passed.\n")); return 1; } return 0; } static int esp_descbc_ivlen(sav) struct secasvar *sav; { if (sav && (sav->flags & SADB_X_EXT_OLD) && (sav->flags & SADB_X_EXT_IV4B)) return 4; if (sav && !(sav->flags & SADB_X_EXT_OLD) && (sav->flags & SADB_X_EXT_DERIV)) return 4; else return 8; } static int esp_descbc_decrypt(m, off, sav, algo, ivlen) struct mbuf *m; size_t off; /* offset to ESP header */ struct secasvar *sav; struct esp_algorithm *algo; int ivlen; { size_t ivoff = 0; size_t bodyoff = 0; u_int8_t *iv; size_t plen; u_int8_t tiv[8]; int derived; + int error; derived = 0; /* sanity check */ if (ivlen != sav->ivlen) { - printf("esp_descbc_decrypt: bad ivlen %d/%d\n", - ivlen, sav->ivlen); + ipseclog((LOG_ERR, "esp_descbc_decrypt: bad ivlen %d/%d\n", + ivlen, sav->ivlen)); return EINVAL; } if (_KEYBITS(sav->key_enc) < algo->keymin || algo->keymax < _KEYBITS(sav->key_enc)) { - printf("esp_descbc_decrypt: bad keylen %d\n", - _KEYBITS(sav->key_enc)); + ipseclog((LOG_ERR, "esp_descbc_decrypt: bad keylen %d\n", + _KEYBITS(sav->key_enc))); return EINVAL; } if (sav->flags & SADB_X_EXT_OLD) { /* RFC 1827 */ ivoff = off + sizeof(struct esp); bodyoff = off + sizeof(struct esp) + ivlen; derived = 0; } else { /* RFC 2406 */ if (sav->flags & SADB_X_EXT_DERIV) { /* * draft-ietf-ipsec-ciph-des-derived-00.txt * uses sequence number field as IV field. * This draft has been deleted, but you can get from * ftp://ftp.kame.net/pub/internet-drafts/. */ ivoff = off + sizeof(struct esp); bodyoff = off + sizeof(struct esp) + sizeof(u_int32_t); ivlen = sizeof(u_int32_t); derived = 1; } else { ivoff = off + sizeof(struct newesp); bodyoff = off + sizeof(struct newesp) + ivlen; derived = 0; } } if (ivlen == 4) { iv = &tiv[0]; m_copydata(m, ivoff, 4, &tiv[0]); m_copydata(m, ivoff, 4, &tiv[4]); tiv[4] ^= 0xff; tiv[5] ^= 0xff; tiv[6] ^= 0xff; tiv[7] ^= 0xff; } else if (ivlen == 8) { iv = &tiv[0]; m_copydata(m, ivoff, 8, &tiv[0]); } else { - printf("esp_descbc_decrypt: unsupported ivlen %d\n", ivlen); + ipseclog((LOG_ERR, "esp_descbc_decrypt: unsupported ivlen %d\n", + ivlen)); return EINVAL; } plen = m->m_pkthdr.len; if (plen < bodyoff) panic("esp_descbc_decrypt: too short packet: len=%lu", (u_long)plen); plen -= bodyoff; if (plen % 8) { - printf("esp_descbc_decrypt: " - "payload length must be multiple of 8\n"); + ipseclog((LOG_ERR, "esp_descbc_decrypt: " + "payload length must be multiple of 8\n")); return EINVAL; } { int deserr; des_key_schedule ks; deserr = des_key_sched((C_Block *)_KEYBUF(sav->key_enc), ks); if (deserr != 0) { - printf("esp_descbc_decrypt: key error %d\n", deserr); + ipseclog((LOG_ERR, + "esp_descbc_decrypt: key error %d\n", deserr)); return EINVAL; } - des_cbc_encrypt(m, bodyoff, plen, ks, (C_Block *)iv, DES_DECRYPT); + error = des_cbc_encrypt(m, bodyoff, plen, ks, (C_Block *)iv, + DES_DECRYPT); /* for safety */ bzero(&ks, sizeof(des_key_schedule)); } /* for safety */ bzero(&tiv[0], sizeof(tiv)); - return 0; + return error; } static int esp_descbc_encrypt(m, off, plen, sav, algo, ivlen) struct mbuf *m; size_t off; /* offset to ESP header */ size_t plen; /* payload length (to be decrypted) */ struct secasvar *sav; struct esp_algorithm *algo; int ivlen; { size_t ivoff = 0; size_t bodyoff = 0; u_int8_t *iv; u_int8_t tiv[8]; int derived; + int error; derived = 0; /* sanity check */ if (plen % 8) { - printf("esp_descbc_encrypt: " - "payload length must be multiple of 8\n"); + ipseclog((LOG_ERR, "esp_descbc_encrypt: " + "payload length must be multiple of 8\n")); return EINVAL; } if (sav->ivlen != ivlen) { - printf("esp_descbc_encrypt: bad ivlen %d/%d\n", - ivlen, sav->ivlen); + ipseclog((LOG_ERR, "esp_descbc_encrypt: bad ivlen %d/%d\n", + ivlen, sav->ivlen)); return EINVAL; } if (_KEYBITS(sav->key_enc) < algo->keymin || algo->keymax < _KEYBITS(sav->key_enc)) { - printf("esp_descbc_encrypt: bad keylen %d\n", - _KEYBITS(sav->key_enc)); + ipseclog((LOG_ERR, "esp_descbc_encrypt: bad keylen %d\n", + _KEYBITS(sav->key_enc))); return EINVAL; } if (sav->flags & SADB_X_EXT_OLD) { /* RFC 1827 */ /* * draft-ietf-ipsec-ciph-des-derived-00.txt * uses sequence number field as IV field. * This draft has been deleted, see above. */ ivoff = off + sizeof(struct esp); bodyoff = off + sizeof(struct esp) + ivlen; derived = 0; } else { /* RFC 2406 */ if (sav->flags & SADB_X_EXT_DERIV) { /* * draft-ietf-ipsec-ciph-des-derived-00.txt * uses sequence number field as IV field. * This draft has been deleted, see above. */ ivoff = off + sizeof(struct esp); bodyoff = off + sizeof(struct esp) + sizeof(u_int32_t); ivlen = sizeof(u_int32_t); derived = 1; } else { ivoff = off + sizeof(struct newesp); bodyoff = off + sizeof(struct newesp) + ivlen; derived = 0; } } if (m->m_pkthdr.len < bodyoff) panic("assumption failed: mbuf too short"); iv = mbuf_find_offset(m, ivoff, ivlen); if (!iv) panic("assumption failed: bad mbuf chain"); if (ivlen == 4) { if (!derived) { bcopy(sav->iv, &tiv[0], 4); bcopy(sav->iv, &tiv[4], 4); tiv[4] ^= 0xff; tiv[5] ^= 0xff; tiv[6] ^= 0xff; tiv[7] ^= 0xff; bcopy(&tiv[0], iv, 4); iv = &tiv[0]; } else { bcopy(iv, &tiv[0], 4); bcopy(iv, &tiv[4], 4); tiv[4] ^= 0xff; tiv[5] ^= 0xff; tiv[6] ^= 0xff; tiv[7] ^= 0xff; iv = &tiv[0]; } } else if (ivlen == 8) bcopy((caddr_t)sav->iv, (caddr_t)iv, ivlen); else { - printf("esp_descbc_encrypt: unsupported ivlen %d\n", ivlen); + ipseclog((LOG_ERR, + "esp_descbc_encrypt: unsupported ivlen %d\n", ivlen)); return EINVAL; } { int deserr; des_key_schedule ks; deserr = des_key_sched((C_Block *)_KEYBUF(sav->key_enc), ks); if (deserr != 0) { - printf("esp_descbc_encrypt: key error %d\n", deserr); + ipseclog((LOG_ERR, + "esp_descbc_encrypt: key error %d\n", deserr)); return EINVAL; } - des_cbc_encrypt(m, bodyoff, plen, ks, (C_Block *)iv, DES_ENCRYPT); + error = des_cbc_encrypt(m, bodyoff, plen, ks, (C_Block *)iv, + DES_ENCRYPT); /* for safety */ bzero(&ks, sizeof(des_key_schedule)); } esp_increment_iv(sav); /* for safety */ bzero(&tiv[0], sizeof(tiv)); - return 0; + return error; } static int esp_cbc_mature(sav) struct secasvar *sav; { int keylen; struct esp_algorithm *algo; if (sav->flags & SADB_X_EXT_OLD) { - printf("esp_cbc_mature: algorithm incompatible with esp-old\n"); + ipseclog((LOG_ERR, + "esp_cbc_mature: algorithm incompatible with esp-old\n")); return 1; } if (sav->flags & SADB_X_EXT_DERIV) { - printf("esp_cbc_mature: algorithm incompatible with derived\n"); + ipseclog((LOG_ERR, + "esp_cbc_mature: algorithm incompatible with derived\n")); return 1; } if (!sav->key_enc) { - printf("esp_cbc_mature: no key is given.\n"); + ipseclog((LOG_ERR, + "esp_cbc_mature: no key is given.\n")); return 1; } algo = &esp_algorithms[sav->alg_enc]; keylen = sav->key_enc->sadb_key_bits; if (keylen < algo->keymin || algo->keymax < keylen) { - printf("esp_cbc_mature: invalid key length %d.\n", - sav->key_enc->sadb_key_bits); + ipseclog((LOG_ERR, "esp_cbc_mature: invalid key length %d.\n", + sav->key_enc->sadb_key_bits)); return 1; } switch (sav->alg_enc) { case SADB_EALG_3DESCBC: /* weak key check */ if (des_is_weak_key((C_Block *)_KEYBUF(sav->key_enc)) || des_is_weak_key((C_Block *)(_KEYBUF(sav->key_enc) + 8)) || des_is_weak_key((C_Block *)(_KEYBUF(sav->key_enc) + 16))) { - printf("esp_cbc_mature: weak key was passed.\n"); + ipseclog((LOG_ERR, + "esp_cbc_mature: weak key was passed.\n")); return 1; } break; case SADB_EALG_BLOWFISHCBC: case SADB_EALG_CAST128CBC: case SADB_EALG_RC5CBC: break; } return 0; } static int esp_blowfish_cbc_decrypt(m, off, sav, algo, ivlen) struct mbuf *m; size_t off; /* offset to ESP header */ struct secasvar *sav; struct esp_algorithm *algo; int ivlen; { size_t ivoff; size_t bodyoff; u_int8_t *iv; u_int8_t tiv[8]; size_t plen; static BF_KEY key; /* made static to avoid kernel stack overflow */ int s; + int error; /* sanity check */ if (sav->ivlen != ivlen) { - printf("esp_blowfish_cbc_decrypt: bad ivlen %d/%d\n", - ivlen, sav->ivlen); + ipseclog((LOG_ERR, + "esp_blowfish_cbc_decrypt: bad ivlen %d/%d\n", + ivlen, sav->ivlen)); return EINVAL; } if (_KEYBITS(sav->key_enc) < algo->keymin || algo->keymax < _KEYBITS(sav->key_enc)) { - printf("esp_blowfish_cbc_decrypt: unsupported key length %d: " - "need %d to %d bits\n", _KEYBITS(sav->key_enc), - algo->keymin, algo->keymax); + ipseclog((LOG_ERR, + "esp_blowfish_cbc_decrypt: unsupported key length %d: " + "need %d to %d bits\n", _KEYBITS(sav->key_enc), + algo->keymin, algo->keymax)); return EINVAL; } if (sav->flags & SADB_X_EXT_OLD) { - printf("esp_blowfish_cbc_decrypt: unsupported ESP version\n"); + ipseclog((LOG_ERR, + "esp_blowfish_cbc_decrypt: unsupported ESP version\n")); return EINVAL; } if (ivlen != 8) { - printf("esp_blowfish_cbc_decrypt: unsupported ivlen %d " - "(this should never happen)\n", ivlen); + ipseclog((LOG_ERR, + "esp_blowfish_cbc_decrypt: unsupported ivlen %d\n", ivlen)); return EINVAL; } ivoff = off + sizeof(struct newesp); bodyoff = off + sizeof(struct newesp) + ivlen; iv = &tiv[0]; m_copydata(m, ivoff, 8, &tiv[0]); plen = m->m_pkthdr.len; if (plen < bodyoff) panic("esp_blowfish_cbc_decrypt: too short packet: len=%lu", (u_long)plen); plen -= bodyoff; if (plen % 8) { - printf("esp_blowfish_cbc_decrypt: " - "payload length must be multiple of 8\n"); + ipseclog((LOG_ERR, "esp_blowfish_cbc_decrypt: " + "payload length must be multiple of 8\n")); return EINVAL; } +#ifdef __NetBSD__ + s = splsoftnet(); /* XXX correct? */ +#else s = splnet(); /* XXX correct? */ +#endif BF_set_key(&key, _KEYBITS(sav->key_enc) / 8, _KEYBUF(sav->key_enc)); - BF_cbc_encrypt_m(m, bodyoff, plen, &key, iv, BF_DECRYPT); + error = BF_cbc_encrypt_m(m, bodyoff, plen, &key, iv, BF_DECRYPT); /* for safety */ bzero(&key, sizeof(BF_KEY)); splx(s); /* for safety */ bzero(&tiv[0], sizeof(tiv)); - return 0; + return error; } static int esp_blowfish_cbc_encrypt(m, off, plen, sav, algo, ivlen) struct mbuf *m; size_t off; /* offset to ESP header */ size_t plen; /* payload length (to be decrypted) */ struct secasvar *sav; struct esp_algorithm *algo; int ivlen; { size_t ivoff; size_t bodyoff; u_int8_t *iv; static BF_KEY key; /* made static to avoid kernel stack overflow */ int s; + int error; /* sanity check */ if (plen % 8) { - printf("esp_blowfish_cbc_encrypt: " - "payload length must be multiple of 8\n"); + ipseclog((LOG_ERR, "esp_blowfish_cbc_encrypt: " + "payload length must be multiple of 8\n")); return EINVAL; } if (sav->ivlen != ivlen) { - printf("esp_blowfish_cbc_encrypt: bad ivlen %d/%d\n", - ivlen, sav->ivlen); + ipseclog((LOG_ERR, + "esp_blowfish_cbc_encrypt: bad ivlen %d/%d\n", + ivlen, sav->ivlen)); return EINVAL; } if (_KEYBITS(sav->key_enc) < algo->keymin || algo->keymax < _KEYBITS(sav->key_enc)) { - printf("esp_blowfish_cbc_encrypt: unsupported key length %d: " - "need %d to %d bits\n", _KEYBITS(sav->key_enc), - algo->keymin, algo->keymax); + ipseclog((LOG_ERR, + "esp_blowfish_cbc_encrypt: unsupported key length %d: " + "need %d to %d bits\n", _KEYBITS(sav->key_enc), + algo->keymin, algo->keymax)); return EINVAL; } if (sav->flags & SADB_X_EXT_OLD) { - printf("esp_blowfish_cbc_encrypt: unsupported ESP version\n"); + ipseclog((LOG_ERR, + "esp_blowfish_cbc_encrypt: unsupported ESP version\n")); return EINVAL; } if (ivlen != 8) { - printf("esp_blowfish_cbc_encrypt: unsupported ivlen %d " - "(this should never happen)\n", ivlen); + ipseclog((LOG_ERR, + "esp_blowfish_cbc_encrypt: unsupported ivlen %d\n", ivlen)); return EINVAL; } ivoff = off + sizeof(struct newesp); bodyoff = off + sizeof(struct newesp) + ivlen; if (m->m_pkthdr.len < bodyoff) panic("assumption failed: mbuf too short"); iv = mbuf_find_offset(m, ivoff, ivlen); if (!iv) panic("assumption failed: bad mbuf chain"); bcopy((caddr_t)sav->iv, (caddr_t)iv, ivlen); +#ifdef __NetBSD__ + s = splsoftnet(); /* XXX correct? */ +#else s = splnet(); /* XXX correct? */ +#endif BF_set_key(&key, _KEYBITS(sav->key_enc) / 8, _KEYBUF(sav->key_enc)); - BF_cbc_encrypt_m(m, bodyoff, plen, &key, iv, BF_ENCRYPT); + error = BF_cbc_encrypt_m(m, bodyoff, plen, &key, iv, BF_ENCRYPT); /* for safety */ bzero(&key, sizeof(BF_KEY)); splx(s); esp_increment_iv(sav); - return 0; + return error; } static int esp_blowfish_cbc_ivlen(sav) struct secasvar *sav; { return 8; } static int esp_cast128cbc_ivlen(sav) struct secasvar *sav; { return 8; } static int esp_cast128cbc_decrypt(m, off, sav, algo, ivlen) struct mbuf *m; size_t off; struct secasvar *sav; struct esp_algorithm *algo; int ivlen; { size_t ivoff; size_t bodyoff; u_int8_t iv[8]; size_t plen; + int error; /* sanity check */ if (ivlen != sav->ivlen) { - printf("esp_cast128cbc_decrypt: bad ivlen %d/%d\n", - ivlen, sav->ivlen); + ipseclog((LOG_ERR, "esp_cast128cbc_decrypt: bad ivlen %d/%d\n", + ivlen, sav->ivlen)); return EINVAL; } if (_KEYBITS(sav->key_enc) < algo->keymin || _KEYBITS(sav->key_enc) > algo->keymax) { - printf("esp_cast128cbc_decrypt: unsupported key length %d: " - "need %d to %d bits\n", _KEYBITS(sav->key_enc), - algo->keymin, algo->keymax); + ipseclog((LOG_ERR, + "esp_cast128cbc_decrypt: unsupported key length %d: " + "need %d to %d bits\n", _KEYBITS(sav->key_enc), + algo->keymin, algo->keymax)); return EINVAL; } if (sav->flags & SADB_X_EXT_OLD) { - printf("esp_cast128cbc_decrypt: unsupported ESP version\n"); + ipseclog((LOG_ERR, + "esp_cast128cbc_decrypt: unsupported ESP version\n")); return EINVAL; } if (ivlen != 8) { - printf("esp_cast128cbc_decrypt: unsupported ivlen %d " - "(this should never happen)\n", ivlen); + ipseclog((LOG_ERR, + "esp_cast128cbc_decrypt: unsupported ivlen %d\n", ivlen)); return EINVAL; } ivoff = off + sizeof(struct newesp); bodyoff = off + sizeof(struct newesp) + ivlen; /* copy mbuf's IV into iv */ m_copydata(m, ivoff, 8, iv); plen = m->m_pkthdr.len; if (plen < bodyoff) { panic("esp_cast128cbc_decrypt: too short packet: len=%lu\n", (u_long)plen); } plen -= bodyoff; if (plen % 8) { - printf("esp_cast128cbc_decrypt: " - "payload length must be multiple of 8\n"); + ipseclog((LOG_ERR, "esp_cast128cbc_decrypt: " + "payload length must be multiple of 8\n")); return EINVAL; } /* decrypt */ { u_int8_t key[16]; u_int32_t subkey[32]; bzero(key, sizeof(key)); bcopy(_KEYBUF(sav->key_enc), key, _KEYLEN(sav->key_enc)); set_cast128_subkey(subkey, key); - cast128_cbc_process(m, bodyoff, plen, subkey, iv, - _KEYBITS(sav->key_enc) / 8, CAST128_DECRYPT); + error = cast128_cbc_process(m, bodyoff, plen, subkey, iv, + _KEYBITS(sav->key_enc) / 8, CAST128_DECRYPT); /* for safety */ bzero(subkey, sizeof(subkey)); bzero(key, sizeof(key)); } - return 0; + return error; } static int esp_cast128cbc_encrypt(m, off, plen, sav, algo, ivlen) struct mbuf *m; size_t off; size_t plen; struct secasvar *sav; struct esp_algorithm *algo; int ivlen; { size_t ivoff; size_t bodyoff; u_int8_t *iv; + int error; /* sanity check */ if (plen % 8) { - printf("esp_cast128cbc_encrypt: " - "payload length must be multiple of 8\n"); + ipseclog((LOG_ERR, "esp_cast128cbc_encrypt: " + "payload length must be multiple of 8\n")); return EINVAL; } if (sav->ivlen != ivlen) { - printf("esp_cast128cbc_encrypt: bad ivlen %d/%d\n", - ivlen, sav->ivlen); + ipseclog((LOG_ERR, "esp_cast128cbc_encrypt: bad ivlen %d/%d\n", + ivlen, sav->ivlen)); return EINVAL; } if (_KEYBITS(sav->key_enc) < algo->keymin || _KEYBITS(sav->key_enc) > algo->keymax) { - printf("esp_cast128cbc_encrypt: unsupported key length %d: " - "needs %d to %d bits\n", _KEYBITS(sav->key_enc), - algo->keymin, algo->keymax); + ipseclog((LOG_ERR, + "esp_cast128cbc_encrypt: unsupported key length %d: " + "needs %d to %d bits\n", _KEYBITS(sav->key_enc), + algo->keymin, algo->keymax)); return EINVAL; } if (sav->flags & SADB_X_EXT_OLD) { - printf("esp_cast128cbc_encrypt: unsupported ESP version\n"); + ipseclog((LOG_ERR, + "esp_cast128cbc_encrypt: unsupported ESP version\n")); return EINVAL; } if (ivlen != 8) { - printf("esp_cast128cbc_encrypt: unsupported ivlen %d " - "(this should never happen)\n", ivlen); + ipseclog((LOG_ERR, + "esp_cast128cbc_encrypt: unsupported ivlen %d\n", ivlen)); return EINVAL; } ivoff = off + sizeof(struct newesp); bodyoff = off + sizeof(struct newesp) + ivlen; if (m->m_pkthdr.len < bodyoff) panic("assumption failed: mbuf too short"); iv = mbuf_find_offset(m, ivoff, ivlen); if (!iv) panic("assumption failed: bad mbuf chain"); bcopy(sav->iv, iv, ivlen); /* encrypt */ { u_int8_t key[16]; u_int32_t subkey[32]; bzero(key, sizeof(key)); bcopy(_KEYBUF(sav->key_enc), key, _KEYLEN(sav->key_enc)); set_cast128_subkey(subkey, key); - cast128_cbc_process(m, bodyoff, plen, subkey, iv, - _KEYBITS(sav->key_enc) / 8, CAST128_ENCRYPT); + error = cast128_cbc_process(m, bodyoff, plen, subkey, iv, + _KEYBITS(sav->key_enc) / 8, CAST128_ENCRYPT); /* for safety */ bzero(subkey, sizeof(subkey)); bzero(key, sizeof(key)); } esp_increment_iv(sav); - return 0; + return error; } static int esp_3descbc_ivlen(sav) struct secasvar *sav; { return 8; } static int esp_3descbc_decrypt(m, off, sav, algo, ivlen) struct mbuf *m; size_t off; struct secasvar *sav; struct esp_algorithm *algo; int ivlen; { size_t ivoff; size_t bodyoff; u_int8_t *iv; size_t plen; u_int8_t tiv[8]; /* sanity check */ if (ivlen != sav->ivlen) { - printf("esp_3descbc_decrypt: bad ivlen %d/%d\n", - ivlen, sav->ivlen); + ipseclog((LOG_ERR, "esp_3descbc_decrypt: bad ivlen %d/%d\n", + ivlen, sav->ivlen)); return EINVAL; } if (_KEYBITS(sav->key_enc) < algo->keymin || algo->keymax < _KEYBITS(sav->key_enc)) { - printf("esp_3descbc_decrypt: bad keylen %d\n", - _KEYBITS(sav->key_enc)); + ipseclog((LOG_ERR, "esp_3descbc_decrypt: bad keylen %d\n", + _KEYBITS(sav->key_enc))); return EINVAL; } if (sav->flags & SADB_X_EXT_OLD) { - printf("esp_3descbc_decrypt: unsupported ESP version\n"); + ipseclog((LOG_ERR, + "esp_3descbc_decrypt: unsupported ESP version\n")); return EINVAL; } if (ivlen != 8) { - printf("esp_3descbc_decrypt: unsupported ivlen %d " - "(this should never happen)\n", ivlen); + ipseclog((LOG_ERR, + "esp_3descbc_decrypt: unsupported ivlen %d\n", ivlen)); return EINVAL; } ivoff = off + sizeof(struct newesp); bodyoff = off + sizeof(struct newesp) + ivlen; iv = &tiv[0]; m_copydata(m, ivoff, 8, &tiv[0]); plen = m->m_pkthdr.len; if (plen < bodyoff) panic("esp_3descbc_decrypt: too short packet: len=%lu", (u_long)plen); plen -= bodyoff; if (plen % 8) { - printf("esp_3descbc_decrypt: " - "payload length must be multiple of 8\n"); + ipseclog((LOG_ERR, "esp_3descbc_decrypt: " + "payload length must be multiple of 8\n")); return EINVAL; } /* decrypt packet */ { int deserr[3]; des_key_schedule ks[3]; deserr[0] = des_key_sched((C_Block *)_KEYBUF(sav->key_enc),ks[0]); deserr[1] = des_key_sched((C_Block *)(_KEYBUF(sav->key_enc) + 8), ks[1]); deserr[2] = des_key_sched((C_Block *)(_KEYBUF(sav->key_enc) + 16), ks[2]); if ((deserr[0] != 0) || (deserr[1] != 0) || (deserr[2] != 0)) { - printf("esp_3descbc_decrypt: key error %d/%d/%d\n", - deserr[0], deserr[1], deserr[2]); + ipseclog((LOG_ERR, "esp_3descbc_decrypt: key error %d/%d/%d\n", + deserr[0], deserr[1], deserr[2])); return EINVAL; } des_3cbc_process(m, bodyoff, plen, ks, (C_Block *)iv, DES_DECRYPT); /* for safety */ bzero(ks[0], sizeof(des_key_schedule)*3); } /* for safety */ bzero(&tiv[0], sizeof(tiv)); return 0; } static int esp_3descbc_encrypt(m, off, plen, sav, algo, ivlen) struct mbuf *m; size_t off; size_t plen; struct secasvar *sav; struct esp_algorithm *algo; int ivlen; { size_t ivoff; size_t bodyoff; u_int8_t *iv; /* sanity check */ if (plen % 8) { - printf("esp_3descbc_encrypt: " - "payload length must be multiple of 8\n"); + ipseclog((LOG_ERR, "esp_3descbc_encrypt: " + "payload length must be multiple of 8\n")); return EINVAL; } if (sav->ivlen != ivlen) { - printf("esp_3descbc_encrypt: bad ivlen %d/%d\n", - ivlen, sav->ivlen); + ipseclog((LOG_ERR, "esp_3descbc_encrypt: bad ivlen %d/%d\n", + ivlen, sav->ivlen)); return EINVAL; } if (_KEYBITS(sav->key_enc) < algo->keymin || algo->keymax < _KEYBITS(sav->key_enc)) { - printf("esp_3descbc_encrypt: bad keylen %d\n", - _KEYBITS(sav->key_enc)); + ipseclog((LOG_ERR, "esp_3descbc_encrypt: bad keylen %d\n", + _KEYBITS(sav->key_enc))); return EINVAL; } if (sav->flags & SADB_X_EXT_OLD) { - printf("esp_3descbc_encrypt: unsupported ESP version\n"); + ipseclog((LOG_ERR, + "esp_3descbc_encrypt: unsupported ESP version\n")); return EINVAL; } if (ivlen != 8) { - printf("esp_3descbc_encrypt: unsupported ivlen %d " - "(this should never happen)\n", ivlen); + ipseclog((LOG_ERR, + "esp_3descbc_encrypt: unsupported ivlen %d\n", ivlen)); return EINVAL; } ivoff = off + sizeof(struct newesp); bodyoff = off + sizeof(struct newesp) + ivlen; if (m->m_pkthdr.len < bodyoff) panic("assumption failed: mbuf too short"); iv = mbuf_find_offset(m, ivoff, ivlen); if (!iv) panic("assumption failed: bad mbuf chain"); bcopy((caddr_t)sav->iv, (caddr_t)iv, ivlen); /* encrypt packet */ { int deserr[3]; des_key_schedule ks[3]; deserr[0] = des_key_sched((C_Block *)_KEYBUF(sav->key_enc), ks[0]); deserr[1] = des_key_sched((C_Block *)(_KEYBUF(sav->key_enc) + 8), ks[1]); deserr[2] = des_key_sched((C_Block *)(_KEYBUF(sav->key_enc) + 16), ks[2]); if ((deserr[0] != 0) || (deserr[1] != 0) || (deserr[2] != 0)) { - printf("esp_3descbc_encrypt: key error %d/%d/%d\n", - deserr[0], deserr[1], deserr[2]); + ipseclog((LOG_ERR, "esp_3descbc_encrypt: key error %d/%d/%d\n", + deserr[0], deserr[1], deserr[2])); return EINVAL; } des_3cbc_process(m, bodyoff, plen, ks, (C_Block *)iv, DES_ENCRYPT); /* for safety */ bzero(ks[0], sizeof(des_key_schedule)*3); } esp_increment_iv(sav); return 0; } static int esp_rc5cbc_ivlen(sav) struct secasvar *sav; { return 8; } static int esp_rc5cbc_decrypt(m, off, sav, algo, ivlen) struct mbuf *m; size_t off; struct secasvar *sav; struct esp_algorithm *algo; int ivlen; { size_t ivoff; size_t bodyoff; u_int8_t iv[8]; size_t plen; + int error; /* sanity check */ if (sav->ivlen != ivlen) { - printf("esp_rc5cbc_decrypt: bad ivlen %d/%d\n", - ivlen, sav->ivlen); + ipseclog((LOG_ERR, "esp_rc5cbc_decrypt: bad ivlen %d/%d\n", + ivlen, sav->ivlen)); return EINVAL; } if ((_KEYBITS(sav->key_enc) < 40) || (_KEYBITS(sav->key_enc) > 2040)) { - printf("esp_rc5cbc_decrypt: unsupported key length %d: " - "need 40 to 2040 bit\n", _KEYBITS(sav->key_enc)); + ipseclog((LOG_ERR, + "esp_rc5cbc_decrypt: unsupported key length %d: " + "need 40 to 2040 bit\n", _KEYBITS(sav->key_enc))); return EINVAL; } if (sav->flags & SADB_X_EXT_OLD) { - printf("esp_rc5cbc_decrypt: unsupported ESP version\n"); + ipseclog((LOG_ERR, + "esp_rc5cbc_decrypt: unsupported ESP version\n")); return EINVAL; } if (ivlen != 8) { - printf("esp_rc5cbc_decrypt: unsupported ivlen %d " - "(this should never happen)\n", ivlen); + ipseclog((LOG_ERR, "esp_rc5cbc_decrypt: unsupported ivlen %d\n", + ivlen)); return EINVAL; } ivoff = off + sizeof(struct newesp); bodyoff = off + sizeof(struct newesp) + ivlen; /* copy mbuf's IV into iv */ m_copydata(m, ivoff, 8, iv); plen = m->m_pkthdr.len; if (plen < bodyoff) { panic("esp_rc5cbc_decrypt: too short packet: len=%lu", (u_long)plen); } plen -= bodyoff; if (plen % 8) { - printf("esp_rc5cbc_decrypt: " - "payload length must be multiple of 8\n"); + ipseclog((LOG_ERR, "esp_rc5cbc_decrypt: " + "payload length must be multiple of 8\n")); return EINVAL; } /* decrypt */ { RC5_WORD e_key[34]; set_rc5_expandkey(e_key, _KEYBUF(sav->key_enc), _KEYBITS(sav->key_enc) / 8, 16); - rc5_cbc_process(m, bodyoff, plen, e_key, iv, RC5_DECRYPT); + error = rc5_cbc_process(m, bodyoff, plen, e_key, iv, RC5_DECRYPT); /* for safety */ bzero(e_key, sizeof(e_key)); } - return 0; + return error; } static int esp_rc5cbc_encrypt(m, off, plen, sav, algo, ivlen) struct mbuf *m; size_t off; size_t plen; struct secasvar *sav; struct esp_algorithm *algo; int ivlen; { size_t ivoff; size_t bodyoff; u_int8_t *iv; + int error; /* sanity check */ if (plen % 8) { - printf("esp_rc5cbc_encrypt: " - "payload length must be multiple of 8\n"); + ipseclog((LOG_ERR, "esp_rc5cbc_encrypt: " + "payload length must be multiple of 8\n")); return EINVAL; } if (sav->ivlen != ivlen) { - printf("esp_rc5cbc_encrypt: bad ivlen %d/%d\n", - ivlen, sav->ivlen); + ipseclog((LOG_ERR, "esp_rc5cbc_encrypt: bad ivlen %d/%d\n", + ivlen, sav->ivlen)); return EINVAL; } if (_KEYBITS(sav->key_enc) < algo->keymin || _KEYBITS(sav->key_enc) > algo->keymax) { - printf("esp_rc5cbc_encrypt: unsupported key length %d: " - "need %d to %d bits\n", _KEYBITS(sav->key_enc), - algo->keymin, algo->keymax); + ipseclog((LOG_ERR, + "esp_rc5cbc_encrypt: unsupported key length %d: " + "need %d to %d bits\n", _KEYBITS(sav->key_enc), + algo->keymin, algo->keymax)); return EINVAL; } if (sav->flags & SADB_X_EXT_OLD) { - printf("esp_rc5cbc_encrypt: unsupported ESP version\n"); + ipseclog((LOG_ERR, + "esp_rc5cbc_encrypt: unsupported ESP version\n")); return EINVAL; } if (ivlen != 8) { - printf("esp_rc5cbc_encrypt: unsupported ivlen %d " - "(this should never happen)\n", ivlen); + ipseclog((LOG_ERR, "esp_rc5cbc_encrypt: unsupported ivlen %d\n", + ivlen)); return EINVAL; } ivoff = off + sizeof(struct newesp); bodyoff = off + sizeof(struct newesp) + ivlen; if (m->m_pkthdr.len < bodyoff) panic("assumption failed: mbuf too short"); iv = mbuf_find_offset(m, ivoff, ivlen); if (!iv) panic("assumption failed: bad mbuf chain"); bcopy(sav->iv, iv, ivlen); /* encrypt */ { RC5_WORD e_key[34]; set_rc5_expandkey(e_key, _KEYBUF(sav->key_enc), _KEYBITS(sav->key_enc) / 8, 16); - rc5_cbc_process(m, bodyoff, plen, e_key, iv, RC5_ENCRYPT); + error = rc5_cbc_process(m, bodyoff, plen, e_key, iv, RC5_ENCRYPT); /* for safety */ bzero(e_key, sizeof(e_key)); } esp_increment_iv(sav); - return 0; + return error; } /* * increment iv. */ static void esp_increment_iv(sav) struct secasvar *sav; { u_int8_t *x; u_int8_t y; int i; +#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) + y = time.tv_sec & 0xff; +#else y = time_second & 0xff; - +#endif if (!y) y++; x = (u_int8_t *)sav->iv; for (i = 0; i < sav->ivlen; i++) { *x = (*x + y) & 0xff; x++; } } static caddr_t mbuf_find_offset(m, off, len) struct mbuf *m; size_t off; size_t len; { struct mbuf *n; size_t cnt; if (m->m_pkthdr.len < off || m->m_pkthdr.len < off + len) return (caddr_t)NULL; cnt = 0; for (n = m; n; n = n->m_next) { if (cnt + n->m_len <= off) { cnt += n->m_len; continue; } if (cnt <= off && off < cnt + n->m_len && cnt <= off + len && off + len <= cnt + n->m_len) { return mtod(n, caddr_t) + off - cnt; } else return (caddr_t)NULL; } return (caddr_t)NULL; } /*------------------------------------------------------------*/ +/* does not free m0 on error */ int esp_auth(m0, skip, length, sav, sum) struct mbuf *m0; size_t skip; /* offset to ESP header */ size_t length; /* payload length */ struct secasvar *sav; u_char *sum; { struct mbuf *m; size_t off; struct ah_algorithm_state s; u_char sumbuf[AH_MAXSUMSIZE]; struct ah_algorithm *algo; size_t siz; + int error; /* sanity checks */ if (m0->m_pkthdr.len < skip) { - printf("esp_auth: mbuf length < skip\n"); + ipseclog((LOG_DEBUG, "esp_auth: mbuf length < skip\n")); return EINVAL; } if (m0->m_pkthdr.len < skip + length) { - printf("esp_auth: mbuf length < skip + length\n"); + ipseclog((LOG_DEBUG, + "esp_auth: mbuf length < skip + length\n")); return EINVAL; } /* * length of esp part (excluding authentication data) must be 4n, * since nexthdr must be at offset 4n+3. */ if (length % 4) { - printf("esp_auth: length is not multiple of 4\n"); + ipseclog((LOG_ERR, "esp_auth: length is not multiple of 4\n")); return EINVAL; } if (!sav) { - printf("esp_auth: NULL SA passed\n"); + ipseclog((LOG_DEBUG, "esp_auth: NULL SA passed\n")); return EINVAL; } if (!sav->alg_auth) { - printf("esp_auth: bad ESP auth algorithm passed: %d\n", sav->alg_auth); + ipseclog((LOG_ERR, + "esp_auth: bad ESP auth algorithm passed: %d\n", + sav->alg_auth)); return EINVAL; } m = m0; off = 0; algo = &ah_algorithms[sav->alg_auth]; siz = (((*algo->sumsiz)(sav) + 3) & ~(4 - 1)); if (sizeof(sumbuf) < siz) { - printf("esp_auth: AH_MAXSUMSIZE is too small: siz=%lu\n", - (u_long)siz); + ipseclog((LOG_DEBUG, + "esp_auth: AH_MAXSUMSIZE is too small: siz=%lu\n", + (u_long)siz)); return EINVAL; } /* skip the header */ while (skip) { if (!m) panic("mbuf chain?"); if (m->m_len <= skip) { skip -= m->m_len; m = m->m_next; off = 0; } else { off = skip; skip = 0; } } - (*algo->init)(&s, sav); + error = (*algo->init)(&s, sav); + if (error) + return error; + while (0 < length) { if (!m) panic("mbuf chain?"); if (m->m_len - off < length) { (*algo->update)(&s, mtod(m, u_char *) + off, m->m_len - off); length -= m->m_len - off; m = m->m_next; off = 0; } else { (*algo->update)(&s, mtod(m, u_char *) + off, length); break; } } (*algo->result)(&s, sumbuf); bcopy(sumbuf, sum, siz); /*XXX*/ - + return 0; } Index: head/sys/netinet6/esp_input.c =================================================================== --- head/sys/netinet6/esp_input.c (revision 62586) +++ head/sys/netinet6/esp_input.c (revision 62587) @@ -1,982 +1,813 @@ +/* $FreeBSD$ */ +/* $KAME: esp_input.c,v 1.25 2000/05/08 08:04:30 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* * RFC1827/2406 Encapsulated Security Payload. */ #include "opt_inet.h" #include "opt_inet6.h" -#include "opt_ipsec.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef INET6 #include #endif #ifdef INET6 -#include +#include #include -#include +#include #endif #include -#include #ifdef INET6 #include +#endif +#include +#ifdef INET6 #include #endif -#ifdef IPSEC_ESP #include #ifdef INET6 #include #endif -#endif #include #include #ifdef IPSEC_DEBUG #include #else -#define KEYDEBUG(lev,arg) +#define KEYDEBUG(lev,arg) #endif -#include - #include #include +#define IPLEN_FLIPPED + #ifdef INET +#include extern struct ipprotosw inetsw[]; +#define ESPMAXLEN \ + (sizeof(struct esp) < sizeof(struct newesp) \ + ? sizeof(struct newesp) : sizeof(struct esp)) + void -esp4_input(m, off, proto) +#if __STDC__ +esp4_input(struct mbuf *m, ...) +#else +esp4_input(m, va_alist) struct mbuf *m; - int off, proto; - + va_dcl +#endif { struct ip *ip; struct esp *esp; - struct esptail *esptail; + struct esptail esptail; u_int32_t spi; struct secasvar *sav = NULL; size_t taillen; u_int16_t nxt; struct esp_algorithm *algo; int ivlen; size_t hlen; size_t esplen; int s; + va_list ap; + int off, proto; + va_start(ap, m); + off = va_arg(ap, int); + proto = va_arg(ap, int); + va_end(ap); + /* sanity check for alignment. */ if (off % 4 != 0 || m->m_pkthdr.len % 4 != 0) { - printf("IPv4 ESP input: packet alignment problem " - "(off=%d, pktlen=%d)\n", off, m->m_pkthdr.len); + ipseclog((LOG_ERR, "IPv4 ESP input: packet alignment problem " + "(off=%d, pktlen=%d)\n", off, m->m_pkthdr.len)); ipsecstat.in_inval++; goto bad; } - if (m->m_len < off + sizeof(struct esp)) { - m = m_pullup(m, off + sizeof(struct esp)); + if (m->m_len < off + ESPMAXLEN) { + m = m_pullup(m, off + ESPMAXLEN); if (!m) { - printf("IPv4 ESP input: can't pullup in esp4_input\n"); + ipseclog((LOG_DEBUG, + "IPv4 ESP input: can't pullup in esp4_input\n")); ipsecstat.in_inval++; goto bad; } } ip = mtod(m, struct ip *); esp = (struct esp *)(((u_int8_t *)ip) + off); #ifdef _IP_VHL hlen = IP_VHL_HL(ip->ip_vhl) << 2; #else hlen = ip->ip_hl << 2; #endif /* find the sassoc. */ spi = esp->esp_spi; if ((sav = key_allocsa(AF_INET, (caddr_t)&ip->ip_src, (caddr_t)&ip->ip_dst, IPPROTO_ESP, spi)) == 0) { - printf("IPv4 ESP input: no key association found for spi %u;" - "dropping the packet for simplicity\n", - (u_int32_t)ntohl(spi)); + ipseclog((LOG_WARNING, + "IPv4 ESP input: no key association found for spi %u\n", + (u_int32_t)ntohl(spi))); ipsecstat.in_nosa++; goto bad; } KEYDEBUG(KEYDEBUG_IPSEC_STAMP, printf("DP esp4_input called to allocate SA:%p\n", sav)); if (sav->state != SADB_SASTATE_MATURE && sav->state != SADB_SASTATE_DYING) { - printf("IPv4 ESP input: non-mature/dying SA found for spi %u; " - "dropping the packet for simplicity\n", - (u_int32_t)ntohl(spi)); + ipseclog((LOG_DEBUG, + "IPv4 ESP input: non-mature/dying SA found for spi %u\n", + (u_int32_t)ntohl(spi))); ipsecstat.in_badspi++; goto bad; } if (sav->alg_enc == SADB_EALG_NONE) { - printf("IPv4 ESP input: unspecified encryption algorithm " - "for spi %u;" - "dropping the packet for simplicity\n", - (u_int32_t)ntohl(spi)); + ipseclog((LOG_DEBUG, "IPv4 ESP input: " + "unspecified encryption algorithm for spi %u\n", + (u_int32_t)ntohl(spi))); ipsecstat.in_badspi++; goto bad; } algo = &esp_algorithms[sav->alg_enc]; /*XXX*/ /* check if we have proper ivlen information */ ivlen = sav->ivlen; if (ivlen < 0) { - log(LOG_NOTICE, "inproper ivlen in IPv4 ESP input: %s %s\n", - ipsec4_logpacketstr(ip, spi), - ipsec_logsastr(sav)); + ipseclog((LOG_ERR, "inproper ivlen in IPv4 ESP input: %s %s\n", + ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav))); ipsecstat.in_inval++; goto bad; } if (!((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay && (sav->alg_auth && sav->key_auth))) goto noreplaycheck; if (sav->alg_auth == SADB_AALG_NULL) goto noreplaycheck; /* * check for sequence number. */ if (ipsec_chkreplay(ntohl(((struct newesp *)esp)->esp_seq), sav)) ; /*okey*/ else { ipsecstat.in_espreplay++; - log(LOG_AUTH, "replay packet in IPv4 ESP input: %s %s\n", - ipsec4_logpacketstr(ip, spi), - ipsec_logsastr(sav)); + ipseclog((LOG_WARNING, + "replay packet in IPv4 ESP input: %s %s\n", + ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav))); goto bad; } /* check ICV */ { - struct mbuf *n; - int len; u_char sum0[AH_MAXSUMSIZE]; u_char sum[AH_MAXSUMSIZE]; struct ah_algorithm *sumalgo; size_t siz; sumalgo = &ah_algorithms[sav->alg_auth]; siz = (((*sumalgo->sumsiz)(sav) + 3) & ~(4 - 1)); if (AH_MAXSUMSIZE < siz) { - printf("internal error: AH_MAXSUMSIZE must be larger than %lu\n", - (u_long)siz); + ipseclog((LOG_DEBUG, + "internal error: AH_MAXSUMSIZE must be larger than %lu\n", + (u_long)siz)); ipsecstat.in_inval++; goto bad; } - n = m; - len = m->m_pkthdr.len; - len -= siz; - while (n && 0 < len) { - if (len < n->m_len) - break; - len -= n->m_len; - n = n->m_next; - } - if (!n) { - printf("mbuf chain problem?\n"); - ipsecstat.in_inval++; - goto bad; - } - m_copydata(n, len, siz, &sum0[0]); + m_copydata(m, m->m_pkthdr.len - siz, siz, &sum0[0]); if (esp_auth(m, off, m->m_pkthdr.len - off - siz, sav, sum)) { - log(LOG_AUTH, "auth fail in IPv4 ESP input: %s %s\n", - ipsec4_logpacketstr(ip, spi), - ipsec_logsastr(sav)); + ipseclog((LOG_WARNING, "auth fail in IPv4 ESP input: %s %s\n", + ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav))); ipsecstat.in_espauthfail++; goto bad; } if (bcmp(sum0, sum, siz) != 0) { - log(LOG_AUTH, "auth fail in IPv4 ESP input: %s %s\n", - ipsec4_logpacketstr(ip, spi), - ipsec_logsastr(sav)); + ipseclog((LOG_WARNING, "auth fail in IPv4 ESP input: %s %s\n", + ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav))); ipsecstat.in_espauthfail++; goto bad; } - /* strip off */ - m->m_pkthdr.len -= siz; - n->m_len -= siz; + /* strip off the authentication data */ + m_adj(m, -siz); ip = mtod(m, struct ip *); +#ifdef IPLEN_FLIPPED ip->ip_len = ip->ip_len - siz; +#else + ip->ip_len = htons(ntohs(ip->ip_len) - siz); +#endif m->m_flags |= M_AUTHIPDGM; ipsecstat.in_espauthsucc++; } /* * update sequence number. */ if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) { - (void)ipsec_updatereplay(ntohl(((struct newesp *)esp)->esp_seq), sav); + if (ipsec_updatereplay(ntohl(((struct newesp *)esp)->esp_seq), sav)) { + ipsecstat.in_espreplay++; + goto bad; + } } noreplaycheck: /* process main esp header. */ if (sav->flags & SADB_X_EXT_OLD) { /* RFC 1827 */ esplen = sizeof(struct esp); } else { /* RFC 2406 */ if (sav->flags & SADB_X_EXT_DERIV) esplen = sizeof(struct esp); else esplen = sizeof(struct newesp); } + if (m->m_pkthdr.len < off + esplen + ivlen + sizeof(esptail)) { + ipseclog((LOG_WARNING, + "IPv4 ESP input: packet too short\n")); + ipsecstat.in_inval++; + goto bad; + } + if (m->m_len < off + esplen + ivlen) { m = m_pullup(m, off + esplen + ivlen); if (!m) { - printf("IPv4 ESP input: can't pullup in esp4_input\n"); + ipseclog((LOG_DEBUG, + "IPv4 ESP input: can't pullup in esp4_input\n")); ipsecstat.in_inval++; goto bad; } } { /* * decrypt the packet. */ if (!algo->decrypt) panic("internal error: no decrypt function"); if ((*algo->decrypt)(m, off, sav, algo, ivlen)) { - log(LOG_AUTH, "decrypt fail in IPv4 ESP input: %s %s\n", - ipsec4_logpacketstr(ip, spi), - ipsec_logsastr(sav)); + ipseclog((LOG_ERR, "decrypt fail in IPv4 ESP input: %s %s\n", + ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav))); ipsecstat.in_inval++; goto bad; } ipsecstat.in_esphist[sav->alg_enc]++; m->m_flags |= M_DECRYPTED; } -#ifdef garbled_data_found_on_mbuf_after_packet - { /* - * For simplicity, we'll trim the packet so that there's no extra - * part appended after IP packet. - * This is rare case for some odd drivers, so there should be no - * performance hit. - */ - - /* - * Note that, in ip_input, ip_len was already flipped and header - * length was subtracted from ip_len. - */ - if (m->m_pkthdr.len != hlen + ip->ip_len) - { - size_t siz; - struct mbuf *n; - - siz = hlen + ip->ip_len; - - /* find the final mbuf */ - for (n = m; n; n = n->m_next) { - if (n->m_len < siz) - siz -= n->m_len; - else - break; - } - if (!n) { - printf("invalid packet\n"); - ipsecstat.in_inval++; - goto bad; - } - - /* trim the final mbuf */ - if (n->m_len < siz) { - printf("invalid size: %d %d\n", n->m_len, siz); - ipsecstat.in_inval++; - goto bad; - } - n->m_len = siz; - - /* dispose the rest of the packet */ - m_freem(n->m_next); - n->m_next = NULL; - - m->m_pkthdr.len = hlen + ip->ip_len; - } - } -#endif - - { - /* * find the trailer of the ESP. */ - struct mbuf *n; /*the last mbuf on the mbuf chain, m_len > 0 */ - struct mbuf *o; /*the last mbuf on the mbuf chain */ + m_copydata(m, m->m_pkthdr.len - sizeof(esptail), sizeof(esptail), + (caddr_t)&esptail); + nxt = esptail.esp_nxt; + taillen = esptail.esp_padlen + sizeof(esptail); - o = m; - n = NULL; - while (o) { - if (0 < o->m_len) - n = o; - o = o->m_next; - } - if (!n || n->m_len < sizeof(struct esptail)) { - printf("IPv4 ESP input: assertion on pad part failed; " - "dropping the packet\n"); - ipsecstat.in_inval++; - goto bad; - } - - esptail = (struct esptail *) - (mtod(n, u_int8_t *) + n->m_len - sizeof(struct esptail)); - nxt = esptail->esp_nxt; - taillen = esptail->esp_padlen + 2; - if (m->m_pkthdr.len < taillen || m->m_pkthdr.len - taillen < hlen) { /*?*/ - log(LOG_NOTICE, "bad pad length in IPv4 ESP input: %s %s\n", - ipsec4_logpacketstr(ip, spi), - ipsec_logsastr(sav)); + ipseclog((LOG_WARNING, + "bad pad length in IPv4 ESP input: %s %s\n", + ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav))); ipsecstat.in_inval++; goto bad; } - /* - * strip off the trailing pad area. - */ - if (taillen < n->m_len) { - /* trailing pad data is included in the last mbuf item. */ - n->m_len -= taillen; - m->m_pkthdr.len -= taillen; - } else { - /* trailing pad data spans on multiple mbuf item. */ - size_t siz; + /* strip off the trailing pad area. */ + m_adj(m, -taillen); - siz = m->m_pkthdr.len; - if (siz < taillen) { - log(LOG_NOTICE, "bad packet length in IPv4 ESP input: %s %s\n", - ipsec4_logpacketstr(ip, spi), - ipsec_logsastr(sav)); - ipsecstat.in_inval++; - goto bad; - } - siz -= taillen; - - /* find the final mbuf */ - for (n = m; n; n = n->m_next) { - if (n->m_len < siz) - siz -= n->m_len; - else - break; - } - if (!n) { - printf("invalid packet\n"); - ipsecstat.in_inval++; - goto bad; - } - - /* trim the final mbuf */ - if (n->m_len < siz) { - printf("invalid size: %d %lu\n", n->m_len, (u_long)siz); - ipsecstat.in_inval++; - goto bad; - } - n->m_len = siz; - - /* dispose the rest of the packet */ - m_freem(n->m_next); - n->m_next = NULL; - - m->m_pkthdr.len -= taillen; - } - +#ifdef IPLEN_FLIPPED ip->ip_len = ip->ip_len - taillen; - } +#else + ip->ip_len = htons(ntohs(ip->ip_len) - taillen); +#endif /* was it transmitted over the IPsec tunnel SA? */ - if (ipsec4_tunnel_validate(ip, nxt, sav) && nxt == IPPROTO_IPV4) { + if (ipsec4_tunnel_validate(ip, nxt, sav)) { /* * strip off all the headers that precedes ESP header. * IP4 xx ESP IP4' payload -> IP4' payload * * XXX more sanity checks * XXX relationship with gif? */ - struct ip oip; /* for debug */ u_int8_t tos; tos = ip->ip_tos; - bcopy(mtod(m, struct ip *), &oip, sizeof(oip)); /* for debug */ m_adj(m, off + esplen + ivlen); if (m->m_len < sizeof(*ip)) { m = m_pullup(m, sizeof(*ip)); if (!m) { ipsecstat.in_inval++; goto bad; } } ip = mtod(m, struct ip *); /* ECN consideration. */ ip_ecn_egress(ip4_ipsec_ecn, &tos, &ip->ip_tos); if (!key_checktunnelsanity(sav, AF_INET, (caddr_t)&ip->ip_src, (caddr_t)&ip->ip_dst)) { - log(LOG_NOTICE, "ipsec tunnel address mismatch in IPv4 ESP input: %s %s\n", - ipsec4_logpacketstr(ip, spi), - ipsec_logsastr(sav)); + ipseclog((LOG_ERR, "ipsec tunnel address mismatch " + "in IPv4 ESP input: %s %s\n", + ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav))); ipsecstat.in_inval++; goto bad; } +#if 0 /* XXX should call ipfw rather than ipsec_in_reject, shouldn't it ? */ + /* drop it if it does not match the default policy */ + if (ipsec4_in_reject(m, NULL)) { + ipsecstat.in_polvio++; + goto bad; + } +#endif + key_sa_recordxfer(sav, m); s = splimp(); if (IF_QFULL(&ipintrq)) { ipsecstat.in_inval++; goto bad; } IF_ENQUEUE(&ipintrq, m); m = NULL; schednetisr(NETISR_IP); /*can be skipped but to make sure*/ splx(s); nxt = IPPROTO_DONE; } else { /* * strip off ESP header and IV. - * We do deep-copy since KAME requires packet to be placed - * in a single mbuf. + * even in m_pulldown case, we need to strip off ESP so that + * we can always compute checksum for AH correctly. */ size_t stripsiz; stripsiz = esplen + ivlen; ip = mtod(m, struct ip *); ovbcopy((caddr_t)ip, (caddr_t)(((u_char *)ip) + stripsiz), off); m->m_data += stripsiz; m->m_len -= stripsiz; m->m_pkthdr.len -= stripsiz; ip = mtod(m, struct ip *); +#ifdef IPLEN_FLIPPED ip->ip_len = ip->ip_len - stripsiz; +#else + ip->ip_len = htons(ntohs(ip->ip_len) - stripsiz); +#endif ip->ip_p = nxt; key_sa_recordxfer(sav, m); if (nxt != IPPROTO_DONE) (*inetsw[ip_protox[nxt]].pr_input)(m, off, nxt); else m_freem(m); m = NULL; } if (sav) { KEYDEBUG(KEYDEBUG_IPSEC_STAMP, printf("DP esp4_input call free SA:%p\n", sav)); key_freesav(sav); } ipsecstat.in_success++; return; bad: if (sav) { KEYDEBUG(KEYDEBUG_IPSEC_STAMP, printf("DP esp4_input call free SA:%p\n", sav)); key_freesav(sav); } if (m) m_freem(m); return; } #endif /* INET */ #ifdef INET6 int esp6_input(mp, offp, proto) struct mbuf **mp; int *offp, proto; { struct mbuf *m = *mp; int off = *offp; struct ip6_hdr *ip6; struct esp *esp; - struct esptail *esptail; + struct esptail esptail; u_int32_t spi; struct secasvar *sav = NULL; size_t taillen; u_int16_t nxt; struct esp_algorithm *algo; int ivlen; size_t esplen; int s; /* sanity check for alignment. */ if (off % 4 != 0 || m->m_pkthdr.len % 4 != 0) { - printf("IPv6 ESP input: packet alignment problem " - "(off=%d, pktlen=%d)\n", off, m->m_pkthdr.len); + ipseclog((LOG_ERR, "IPv6 ESP input: packet alignment problem " + "(off=%d, pktlen=%d)\n", off, m->m_pkthdr.len)); ipsec6stat.in_inval++; goto bad; } - IP6_EXTHDR_CHECK(m, off, sizeof(struct esp), IPPROTO_DONE); - +#ifndef PULLDOWN_TEST + IP6_EXTHDR_CHECK(m, off, ESPMAXLEN, IPPROTO_DONE); + esp = (struct esp *)(mtod(m, caddr_t) + off); +#else + IP6_EXTHDR_GET(esp, struct esp *, m, off, ESPMAXLEN); + if (esp == NULL) { + ipsec6stat.in_inval++; + return IPPROTO_DONE; + } +#endif ip6 = mtod(m, struct ip6_hdr *); - esp = (struct esp *)(((u_int8_t *)ip6) + off); if (ntohs(ip6->ip6_plen) == 0) { - printf("IPv6 ESP input: ESP with IPv6 jumbogram is not supported.\n"); + ipseclog((LOG_ERR, "IPv6 ESP input: " + "ESP with IPv6 jumbogram is not supported.\n")); ipsec6stat.in_inval++; goto bad; } /* find the sassoc. */ spi = esp->esp_spi; if ((sav = key_allocsa(AF_INET6, (caddr_t)&ip6->ip6_src, (caddr_t)&ip6->ip6_dst, IPPROTO_ESP, spi)) == 0) { - printf("IPv6 ESP input: no key association found for spi %u;" - "dropping the packet for simplicity\n", - (u_int32_t)ntohl(spi)); + ipseclog((LOG_WARNING, + "IPv6 ESP input: no key association found for spi %u\n", + (u_int32_t)ntohl(spi))); ipsec6stat.in_nosa++; goto bad; } KEYDEBUG(KEYDEBUG_IPSEC_STAMP, printf("DP esp6_input called to allocate SA:%p\n", sav)); if (sav->state != SADB_SASTATE_MATURE && sav->state != SADB_SASTATE_DYING) { - printf("IPv6 ESP input: non-mature/dying SA found for spi %u; " - "dropping the packet for simplicity\n", - (u_int32_t)ntohl(spi)); + ipseclog((LOG_DEBUG, + "IPv6 ESP input: non-mature/dying SA found for spi %u\n", + (u_int32_t)ntohl(spi))); ipsec6stat.in_badspi++; goto bad; } if (sav->alg_enc == SADB_EALG_NONE) { - printf("IPv6 ESP input: unspecified encryption algorithm " - "for spi %u;" - "dropping the packet for simplicity\n", - (u_int32_t)ntohl(spi)); + ipseclog((LOG_DEBUG, "IPv6 ESP input: " + "unspecified encryption algorithm for spi %u\n", + (u_int32_t)ntohl(spi))); ipsec6stat.in_badspi++; goto bad; } algo = &esp_algorithms[sav->alg_enc]; /*XXX*/ /* check if we have proper ivlen information */ ivlen = sav->ivlen; if (ivlen < 0) { - log(LOG_NOTICE, "inproper ivlen in IPv6 ESP input: %s %s\n", - ipsec6_logpacketstr(ip6, spi), - ipsec_logsastr(sav)); + ipseclog((LOG_ERR, "inproper ivlen in IPv6 ESP input: %s %s\n", + ipsec6_logpacketstr(ip6, spi), ipsec_logsastr(sav))); ipsec6stat.in_badspi++; goto bad; } if (!((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay && (sav->alg_auth && sav->key_auth))) goto noreplaycheck; if (sav->alg_auth == SADB_AALG_NULL) goto noreplaycheck; /* * check for sequence number. */ if (ipsec_chkreplay(ntohl(((struct newesp *)esp)->esp_seq), sav)) ; /*okey*/ else { ipsec6stat.in_espreplay++; - log(LOG_AUTH, "replay packet in IPv6 ESP input: %s %s\n", - ipsec6_logpacketstr(ip6, spi), - ipsec_logsastr(sav)); + ipseclog((LOG_WARNING, + "replay packet in IPv6 ESP input: %s %s\n", + ipsec6_logpacketstr(ip6, spi), ipsec_logsastr(sav))); goto bad; } /* check ICV */ { - struct mbuf *n; - size_t len; u_char sum0[AH_MAXSUMSIZE]; u_char sum[AH_MAXSUMSIZE]; struct ah_algorithm *sumalgo; size_t siz; sumalgo = &ah_algorithms[sav->alg_auth]; siz = (((*sumalgo->sumsiz)(sav) + 3) & ~(4 - 1)); if (AH_MAXSUMSIZE < siz) { - printf("internal error: AH_MAXSUMSIZE must be larger than %lu\n", - (u_long)siz); + ipseclog((LOG_DEBUG, + "internal error: AH_MAXSUMSIZE must be larger than %lu\n", + (u_long)siz)); ipsec6stat.in_inval++; goto bad; } - n = m; - len = m->m_pkthdr.len; - len -= siz; /*XXX*/ - while (n && 0 < len) { - if (len < n->m_len) - break; - len -= n->m_len; - n = n->m_next; - } - if (!n) { - printf("mbuf chain problem?\n"); - ipsec6stat.in_inval++; - goto bad; - } - m_copydata(n, len, siz, &sum0[0]); + m_copydata(m, m->m_pkthdr.len - siz, siz, &sum0[0]); if (esp_auth(m, off, m->m_pkthdr.len - off - siz, sav, sum)) { - log(LOG_AUTH, "auth fail in IPv6 ESP input: %s %s\n", - ipsec6_logpacketstr(ip6, spi), - ipsec_logsastr(sav)); + ipseclog((LOG_WARNING, "auth fail in IPv6 ESP input: %s %s\n", + ipsec6_logpacketstr(ip6, spi), ipsec_logsastr(sav))); ipsec6stat.in_espauthfail++; goto bad; } if (bcmp(sum0, sum, siz) != 0) { - log(LOG_AUTH, "auth fail in IPv6 ESP input: %s %s\n", - ipsec6_logpacketstr(ip6, spi), - ipsec_logsastr(sav)); + ipseclog((LOG_WARNING, "auth fail in IPv6 ESP input: %s %s\n", + ipsec6_logpacketstr(ip6, spi), ipsec_logsastr(sav))); ipsec6stat.in_espauthfail++; goto bad; } - /* strip off */ - m->m_pkthdr.len -= siz; - n->m_len -= siz; + /* strip off the authentication data */ + m_adj(m, -siz); ip6 = mtod(m, struct ip6_hdr *); ip6->ip6_plen = htons(ntohs(ip6->ip6_plen) - siz); m->m_flags |= M_AUTHIPDGM; ipsec6stat.in_espauthsucc++; } /* * update sequence number. */ if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) { - (void)ipsec_updatereplay(ntohl(((struct newesp *)esp)->esp_seq), sav); + if (ipsec_updatereplay(ntohl(((struct newesp *)esp)->esp_seq), sav)) { + ipsec6stat.in_espreplay++; + goto bad; + } } noreplaycheck: /* process main esp header. */ if (sav->flags & SADB_X_EXT_OLD) { /* RFC 1827 */ esplen = sizeof(struct esp); } else { /* RFC 2406 */ if (sav->flags & SADB_X_EXT_DERIV) esplen = sizeof(struct esp); else esplen = sizeof(struct newesp); } + if (m->m_pkthdr.len < off + esplen + ivlen + sizeof(esptail)) { + ipseclog((LOG_WARNING, + "IPv6 ESP input: packet too short\n")); + ipsec6stat.in_inval++; + goto bad; + } + +#ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, off, esplen + ivlen, IPPROTO_DONE); /*XXX*/ +#else + IP6_EXTHDR_GET(esp, struct esp *, m, off, esplen + ivlen); + if (esp == NULL) { + ipsec6stat.in_inval++; + m = NULL; + goto bad; + } +#endif + ip6 = mtod(m, struct ip6_hdr *); /*set it again just in case*/ /* * decrypt the packet. */ if (!algo->decrypt) panic("internal error: no decrypt function"); if ((*algo->decrypt)(m, off, sav, algo, ivlen)) { - log(LOG_AUTH, "decrypt fail in IPv6 ESP input: %s %s\n", - ipsec6_logpacketstr(ip6, spi), - ipsec_logsastr(sav)); + ipseclog((LOG_ERR, "decrypt fail in IPv6 ESP input: %s %s\n", + ipsec6_logpacketstr(ip6, spi), ipsec_logsastr(sav))); ipsec6stat.in_inval++; goto bad; } ipsec6stat.in_esphist[sav->alg_enc]++; m->m_flags |= M_DECRYPTED; -#ifdef garbled_data_found_on_mbuf_after_packet - { /* - * For simplicity, we'll trim the packet so that there's no extra - * part appended after IP packet. - * This is rare case for some odd drivers, so there should be no - * performance hit. - */ - - /* - * Note that, in ip_input, ip_len was already flipped and header - * length was subtracted from ip_len. - */ - if (m->m_pkthdr.len != hlen + ip->ip_len) - { - size_t siz; - struct mbuf *n; - - siz = hlen + ip->ip_len; - - /* find the final mbuf */ - for (n = m; n; n = n->m_next) { - if (n->m_len < siz) - siz -= n->m_len; - else - break; - } - if (!n) { - printf("invalid packet\n"); - ipsec6stat.in_inval++; - goto bad; - } - - /* trim the final mbuf */ - if (n->m_len < siz) { - printf("invalid size: %d %d\n", n->m_len, siz); - ipsec6stat.in_inval++; - goto bad; - } - n->m_len = siz; - - /* dispose the rest of the packet */ - m_freem(n->m_next); - n->m_next = NULL; - - m->m_pkthdr.len = hlen + ip->ip_len; - } - } -#endif - - { - /* * find the trailer of the ESP. */ - struct mbuf *n; /*the last mbuf on the mbuf chain, m_len > 0 */ - struct mbuf *o; /*the last mbuf on the mbuf chain */ + m_copydata(m, m->m_pkthdr.len - sizeof(esptail), sizeof(esptail), + (caddr_t)&esptail); + nxt = esptail.esp_nxt; + taillen = esptail.esp_padlen + sizeof(esptail); - o = m; - n = NULL; - while (o) { - if (0 < o->m_len) - n = o; - o = o->m_next; - } - if (!n || n->m_len < sizeof(struct esptail)) { - printf("IPv6 ESP input: assertion on pad part failed; " - "dropping the packet\n"); - ipsec6stat.in_inval++; - goto bad; - } - - esptail = (struct esptail *) - (mtod(n, u_int8_t *) + n->m_len - sizeof(struct esptail)); - nxt = esptail->esp_nxt; - taillen = esptail->esp_padlen + 2; - if (m->m_pkthdr.len < taillen || m->m_pkthdr.len - taillen < sizeof(struct ip6_hdr)) { /*?*/ - log(LOG_NOTICE, "bad pad length in IPv6 ESP input: %s %s\n", - ipsec6_logpacketstr(ip6, spi), - ipsec_logsastr(sav)); + ipseclog((LOG_WARNING, + "bad pad length in IPv6 ESP input: %s %s\n", + ipsec6_logpacketstr(ip6, spi), ipsec_logsastr(sav))); ipsec6stat.in_inval++; goto bad; } - /* - * XXX strip off the padding. - */ - if (taillen < n->m_len) { - /* trailing pad data is included in the last mbuf item. */ - n->m_len -= taillen; - m->m_pkthdr.len -= taillen; - } else { - /* trailing pad data spans on multiple mbuf item. */ - size_t siz; + /* strip off the trailing pad area. */ + m_adj(m, -taillen); - siz = m->m_pkthdr.len; - if (siz < taillen) { - log(LOG_NOTICE, "bad packet length in IPv6 ESP input: %s %s\n", - ipsec6_logpacketstr(ip6, spi), - ipsec_logsastr(sav)); - ipsec6stat.in_inval++; - goto bad; - } - siz -= taillen; - - /* find the final mbuf */ - for (n = m; n; n = n->m_next) { - if (n->m_len < siz) - siz -= n->m_len; - else - break; - } - if (!n) { - printf("invalid packet\n"); - ipsec6stat.in_inval++; - goto bad; - } - - /* trim the final mbuf */ - if (n->m_len < siz) { - printf("invalid size: %d %lu\n", n->m_len, (u_long)siz); - ipsec6stat.in_inval++; - goto bad; - } - n->m_len = siz; - - /* dispose the rest of the packet */ - m_freem(n->m_next); - n->m_next = NULL; - - m->m_pkthdr.len -= taillen; - } - ip6->ip6_plen = htons(ntohs(ip6->ip6_plen) - taillen); - } /* was it transmitted over the IPsec tunnel SA? */ - if (ipsec6_tunnel_validate(ip6, nxt, sav) && nxt == IPPROTO_IPV6) { + if (ipsec6_tunnel_validate(ip6, nxt, sav)) { /* * strip off all the headers that precedes ESP header. * IP6 xx ESP IP6' payload -> IP6' payload * * XXX more sanity checks * XXX relationship with gif? */ u_int32_t flowinfo; /*net endian*/ flowinfo = ip6->ip6_flow; m_adj(m, off + esplen + ivlen); if (m->m_len < sizeof(*ip6)) { +#ifndef PULLDOWN_TEST /* * m_pullup is prohibited in KAME IPv6 input processing * but there's no other way! */ +#else + /* okay to pullup in m_pulldown style */ +#endif m = m_pullup(m, sizeof(*ip6)); if (!m) { ipsec6stat.in_inval++; goto bad; } } ip6 = mtod(m, struct ip6_hdr *); /* ECN consideration. */ ip6_ecn_egress(ip6_ipsec_ecn, &flowinfo, &ip6->ip6_flow); if (!key_checktunnelsanity(sav, AF_INET6, (caddr_t)&ip6->ip6_src, (caddr_t)&ip6->ip6_dst)) { - log(LOG_NOTICE, "ipsec tunnel address mismatch in IPv6 ESP input: %s %s\n", - ipsec6_logpacketstr(ip6, spi), - ipsec_logsastr(sav)); + ipseclog((LOG_ERR, "ipsec tunnel address mismatch " + "in IPv6 ESP input: %s %s\n", + ipsec6_logpacketstr(ip6, spi), + ipsec_logsastr(sav))); ipsec6stat.in_inval++; goto bad; } +#if 0 /* XXX should call ipfw rather than ipsec_in_reject, shouldn't it ? */ + /* drop it if it does not match the default policy */ + if (ipsec6_in_reject(m, NULL)) { + ipsec6stat.in_polvio++; + goto bad; + } +#endif + key_sa_recordxfer(sav, m); s = splimp(); if (IF_QFULL(&ip6intrq)) { ipsec6stat.in_inval++; goto bad; } IF_ENQUEUE(&ip6intrq, m); m = NULL; schednetisr(NETISR_IPV6); /*can be skipped but to make sure*/ splx(s); nxt = IPPROTO_DONE; } else { /* * strip off ESP header and IV. - * We do deep-copy since KAME requires packet to be placed - * in a single mbuf. + * even in m_pulldown case, we need to strip off ESP so that + * we can always compute checksum for AH correctly. */ size_t stripsiz; char *prvnxtp; /* * Set the next header field of the previous header correctly. */ prvnxtp = ip6_get_prevhdr(m, off); /* XXX */ *prvnxtp = nxt; stripsiz = esplen + ivlen; ip6 = mtod(m, struct ip6_hdr *); - ovbcopy((caddr_t)ip6, (caddr_t)(((u_char *)ip6) + stripsiz), - off); - m->m_data += stripsiz; - m->m_len -= stripsiz; - m->m_pkthdr.len -= stripsiz; + if (m->m_len >= stripsiz + off) { + ovbcopy((caddr_t)ip6, ((caddr_t)ip6) + stripsiz, off); + m->m_data += stripsiz; + m->m_len -= stripsiz; + m->m_pkthdr.len -= stripsiz; + } else { + /* + * this comes with no copy if the boundary is on + * cluster + */ + struct mbuf *n; + + n = m_split(m, off, M_DONTWAIT); + if (n == NULL) { + /* m is retained by m_split */ + goto bad; + } + m_adj(n, stripsiz); + m_cat(m, n); + /* m_cat does not update m_pkthdr.len */ + m->m_pkthdr.len += n->m_pkthdr.len; + } ip6 = mtod(m, struct ip6_hdr *); ip6->ip6_plen = htons(ntohs(ip6->ip6_plen) - stripsiz); key_sa_recordxfer(sav, m); } *offp = off; *mp = m; if (sav) { KEYDEBUG(KEYDEBUG_IPSEC_STAMP, printf("DP esp6_input call free SA:%p\n", sav)); key_freesav(sav); } ipsec6stat.in_success++; return nxt; bad: if (sav) { KEYDEBUG(KEYDEBUG_IPSEC_STAMP, printf("DP esp6_input call free SA:%p\n", sav)); key_freesav(sav); } if (m) m_freem(m); return IPPROTO_DONE; } #endif /* INET6 */ Index: head/sys/netinet6/esp_output.c =================================================================== --- head/sys/netinet6/esp_output.c (revision 62586) +++ head/sys/netinet6/esp_output.c (revision 62587) @@ -1,681 +1,692 @@ +/* $FreeBSD$ */ +/* $KAME: esp_output.c,v 1.22 2000/07/03 13:23:28 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ #include "opt_inet.h" #include "opt_inet6.h" -#include "opt_ipsec.h" /* * RFC1827/2406 Encapsulated Security Payload. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #ifdef INET6 -#include +#include #include -#include +#include #endif #include -#include #ifdef INET6 #include +#endif +#include +#ifdef INET6 #include #endif -#ifdef IPSEC_ESP #include #ifdef INET6 #include #endif -#endif #include #include -#ifdef IPSEC_DEBUG -#include -#else -#define KEYDEBUG(lev,arg) -#endif #include static int esp_output __P((struct mbuf *, u_char *, struct mbuf *, struct ipsecrequest *, int)); /* * compute ESP header size. */ size_t esp_hdrsiz(isr) struct ipsecrequest *isr; { struct secasvar *sav; struct esp_algorithm *algo; size_t ivlen; size_t authlen; size_t hdrsiz; /* sanity check */ if (isr == NULL) panic("esp_hdrsiz: NULL was passed.\n"); sav = isr->sav; if (isr->saidx.proto != IPPROTO_ESP) panic("unsupported mode passed to esp_hdrsiz"); if (sav == NULL) - goto contrive; + goto estimate; if (sav->state != SADB_SASTATE_MATURE && sav->state != SADB_SASTATE_DYING) - goto contrive; + goto estimate; /* we need transport mode ESP. */ algo = &esp_algorithms[sav->alg_enc]; if (!algo) - goto contrive; + goto estimate; ivlen = sav->ivlen; if (ivlen < 0) - goto contrive; + goto estimate; /* * XXX * right now we don't calcurate the padding size. simply * treat the padding size as constant, for simplicity. * * XXX variable size padding support */ if (sav->flags & SADB_X_EXT_OLD) { /* RFC 1827 */ hdrsiz = sizeof(struct esp) + ivlen + 9; } else { /* RFC 2406 */ if (sav->replay && sav->alg_auth && sav->key_auth) authlen = (*ah_algorithms[sav->alg_auth].sumsiz)(sav); else authlen = 0; hdrsiz = sizeof(struct newesp) + ivlen + 9 + authlen; } return hdrsiz; - contrive: + estimate: /* * ASSUMING: * sizeof(struct newesp) > sizeof(struct esp). * 8 = ivlen for CBC mode (RFC2451). - * 9 = (maximum padding length without random Padding length) + * 9 = (maximum padding length without random padding length) * + (Pad Length field) + (Next Header field). - * 16 = maximum ICV we supported. + * 16 = maximum ICV we support. */ return sizeof(struct newesp) + 8 + 9 + 16; } /* * Modify the packet so that the payload is encrypted. * The mbuf (m) must start with IPv4 or IPv6 header. * On failure, free the given mbuf and return NULL. * * on invocation: * m nexthdrp md * v v v * IP ......... payload * during the encryption: * m nexthdrp mprev md * v v v v * IP ............... esp iv payload pad padlen nxthdr * <--><-><------><---------------> * esplen plen extendsiz * ivlen * <-----> esphlen * <-> hlen * <-----------------> espoff */ static int esp_output(m, nexthdrp, md, isr, af) struct mbuf *m; u_char *nexthdrp; struct mbuf *md; struct ipsecrequest *isr; int af; { struct mbuf *n; struct mbuf *mprev; struct esp *esp; struct esptail *esptail; struct secasvar *sav = isr->sav; struct esp_algorithm *algo; u_int32_t spi; u_int8_t nxt = 0; size_t plen; /*payload length to be encrypted*/ size_t espoff; int ivlen; int afnumber; size_t extendsiz; int error = 0; switch (af) { #ifdef INET case AF_INET: afnumber = 4; break; #endif #ifdef INET6 case AF_INET6: afnumber = 6; break; #endif default: - printf("esp_output: unsupported af %d\n", af); + ipseclog((LOG_ERR, "esp_output: unsupported af %d\n", af)); return 0; /* no change at all */ } /* some sanity check */ if ((sav->flags & SADB_X_EXT_OLD) == 0 && !sav->replay) { switch (af) { #ifdef INET case AF_INET: { struct ip *ip; ip = mtod(m, struct ip *); - printf("esp4_output: internal error: " - "sav->replay is null: " - "%x->%x, SPI=%u\n", + ipseclog((LOG_DEBUG, "esp4_output: internal error: " + "sav->replay is null: %x->%x, SPI=%u\n", (u_int32_t)ntohl(ip->ip_src.s_addr), (u_int32_t)ntohl(ip->ip_dst.s_addr), - (u_int32_t)ntohl(sav->spi)); + (u_int32_t)ntohl(sav->spi))); ipsecstat.out_inval++; m_freem(m); return EINVAL; } #endif /*INET*/ #ifdef INET6 case AF_INET6: { struct ip6_hdr *ip6; ip6 = mtod(m, struct ip6_hdr *); - printf("esp6_output: internal error: " + ipseclog((LOG_DEBUG, "esp6_output: internal error: " "sav->replay is null: SPI=%u\n", - (u_int32_t)ntohl(sav->spi)); + (u_int32_t)ntohl(sav->spi))); ipsec6stat.out_inval++; m_freem(m); return EINVAL; } #endif /*INET6*/ } } algo = &esp_algorithms[sav->alg_enc]; /*XXX*/ spi = sav->spi; ivlen = sav->ivlen; /* should be okey */ if (ivlen < 0) { panic("invalid ivlen"); } { /* * insert ESP header. * XXX inserts ESP header right after IPv4 header. should * chase the header chain. * XXX sequential number */ #ifdef INET struct ip *ip = NULL; #endif #ifdef INET6 struct ip6_hdr *ip6 = NULL; #endif size_t esplen; /*sizeof(struct esp/newesp)*/ size_t esphlen; /*sizeof(struct esp/newesp) + ivlen*/ size_t hlen = 0; /*ip header len*/ if (sav->flags & SADB_X_EXT_OLD) { /* RFC 1827 */ esplen = sizeof(struct esp); } else { /* RFC 2406 */ if (sav->flags & SADB_X_EXT_DERIV) esplen = sizeof(struct esp); else esplen = sizeof(struct newesp); } esphlen = esplen + ivlen; for (mprev = m; mprev && mprev->m_next != md; mprev = mprev->m_next) ; if (mprev == NULL || mprev->m_next != md) { - printf("esp%d_output: md is not in chain\n", afnumber); + ipseclog((LOG_DEBUG, "esp%d_output: md is not in chain\n", + afnumber)); m_freem(m); return EINVAL; } plen = 0; for (n = md; n; n = n->m_next) plen += n->m_len; switch (af) { #ifdef INET case AF_INET: ip = mtod(m, struct ip *); #ifdef _IP_VHL hlen = IP_VHL_HL(ip->ip_vhl) << 2; #else hlen = ip->ip_hl << 2; #endif break; #endif #ifdef INET6 case AF_INET6: ip6 = mtod(m, struct ip6_hdr *); hlen = sizeof(*ip6); break; #endif } /* make the packet over-writable */ mprev->m_next = NULL; if ((md = ipsec_copypkt(md)) == NULL) { m_freem(m); error = ENOBUFS; goto fail; } mprev->m_next = md; espoff = m->m_pkthdr.len - plen; /* * grow the mbuf to accomodate ESP header. * before: IP ... payload * after: IP ... ESP IV payload */ if (M_LEADINGSPACE(md) < esphlen) { MGET(n, M_DONTWAIT, MT_DATA); if (!n) { m_freem(m); error = ENOBUFS; goto fail; } n->m_len = esphlen; mprev->m_next = n; n->m_next = md; m->m_pkthdr.len += esphlen; esp = mtod(n, struct esp *); } else { md->m_len += esphlen; md->m_data -= esphlen; m->m_pkthdr.len += esphlen; esp = mtod(md, struct esp *); } - + nxt = *nexthdrp; *nexthdrp = IPPROTO_ESP; switch (af) { #ifdef INET case AF_INET: if (esphlen < (IP_MAXPACKET - ntohs(ip->ip_len))) ip->ip_len = htons(ntohs(ip->ip_len) + esphlen); else { - printf("IPv4 ESP output: size exceeds limit\n"); + ipseclog((LOG_ERR, + "IPv4 ESP output: size exceeds limit\n")); ipsecstat.out_inval++; m_freem(m); error = EMSGSIZE; goto fail; } break; #endif #ifdef INET6 case AF_INET6: /* total packet length will be computed in ip6_output() */ break; #endif } } /* initialize esp header. */ esp->esp_spi = spi; if ((sav->flags & SADB_X_EXT_OLD) == 0) { struct newesp *nesp; nesp = (struct newesp *)esp; + if (sav->replay->count == ~0) { + if ((sav->flags & SADB_X_EXT_CYCSEQ) == 0) { + /* XXX Is it noisy ? */ + ipseclog((LOG_WARNING, + "replay counter overflowed. %s\n", + ipsec_logsastr(sav))); + ipsecstat.out_inval++; + m_freem(m); + return EINVAL; + } + } sav->replay->count++; /* * XXX sequence number must not be cycled, if the SA is * installed by IKE daemon. */ nesp->esp_seq = htonl(sav->replay->count); } { /* * find the last mbuf. make some room for ESP trailer. * XXX new-esp authentication data */ #ifdef INET struct ip *ip = NULL; #endif size_t padbound; + u_char *extend; + int i; if (algo->padbound) padbound = algo->padbound; else padbound = 4; /* ESP packet, including nxthdr field, must be length of 4n */ if (padbound < 4) padbound = 4; - + extendsiz = padbound - (plen % padbound); if (extendsiz == 1) extendsiz = padbound + 1; n = m; while (n->m_next) n = n->m_next; /* * if M_EXT, the external part may be shared among * two consequtive TCP packets. */ if (!(n->m_flags & M_EXT) && extendsiz < M_TRAILINGSPACE(n)) { - switch (sav->flags & SADB_X_EXT_PMASK) { - case SADB_X_EXT_PRAND: - break; - case SADB_X_EXT_PZERO: - bzero((caddr_t)(mtod(n, u_int8_t *) + n->m_len), - extendsiz); - break; - case SADB_X_EXT_PSEQ: - { - int i; - u_char *p; - p = mtod(n, u_char *) + n->m_len; - for (i = 0; i < extendsiz; i++) - p[i] = i + 1; - break; - } - } + extend = mtod(n, u_char *) + n->m_len; n->m_len += extendsiz; m->m_pkthdr.len += extendsiz; } else { struct mbuf *nn; MGET(nn, M_DONTWAIT, MT_DATA); if (!nn) { - printf("esp%d_output: can't alloc mbuf", afnumber); + ipseclog((LOG_DEBUG, "esp%d_output: can't alloc mbuf", + afnumber)); m_freem(m); error = ENOBUFS; goto fail; } + extend = mtod(nn, u_char *); nn->m_len = extendsiz; - switch (sav->flags & SADB_X_EXT_PMASK) { - case SADB_X_EXT_PRAND: - break; - case SADB_X_EXT_PZERO: - bzero(mtod(nn, caddr_t), extendsiz); - break; - case SADB_X_EXT_PSEQ: - { - int i; - u_char *p; - p = mtod(nn, u_char *); - for (i = 0; i < extendsiz; i++) - p[i] = i + 1; - break; - } - } nn->m_next = NULL; n->m_next = nn; n = nn; m->m_pkthdr.len += extendsiz; } + switch (sav->flags & SADB_X_EXT_PMASK) { + case SADB_X_EXT_PRAND: + for (i = 0; i < extendsiz; i++) + extend[i] = random() & 0xff; + break; + case SADB_X_EXT_PZERO: + bzero(extend, extendsiz); + break; + case SADB_X_EXT_PSEQ: + for (i = 0; i < extendsiz; i++) + extend[i] = (i + 1) & 0xff; + break; + } /* initialize esp trailer. */ esptail = (struct esptail *) (mtod(n, u_int8_t *) + n->m_len - sizeof(struct esptail)); esptail->esp_nxt = nxt; esptail->esp_padlen = extendsiz - 2; /* modify IP header (for ESP header part only) */ switch (af) { #ifdef INET case AF_INET: ip = mtod(m, struct ip *); if (extendsiz < (IP_MAXPACKET - ntohs(ip->ip_len))) ip->ip_len = htons(ntohs(ip->ip_len) + extendsiz); else { - printf("IPv4 ESP output: size exceeds limit\n"); + ipseclog((LOG_ERR, + "IPv4 ESP output: size exceeds limit\n")); ipsecstat.out_inval++; m_freem(m); error = EMSGSIZE; goto fail; } break; #endif #ifdef INET6 case AF_INET6: /* total packet length will be computed in ip6_output() */ break; #endif } } /* * encrypt the packet, based on security association * and the algorithm specified. */ if (!algo->encrypt) panic("internal error: no encrypt function"); if ((*algo->encrypt)(m, espoff, plen + extendsiz, sav, algo, ivlen)) { - printf("packet encryption failure\n"); + ipseclog((LOG_ERR, "packet encryption failure\n")); m_freem(m); switch (af) { #ifdef INET case AF_INET: ipsecstat.out_inval++; break; #endif #ifdef INET6 case AF_INET6: ipsec6stat.out_inval++; break; #endif } error = EINVAL; goto fail; } /* * calculate ICV if required. */ if (!sav->replay) goto noantireplay; if (!sav->key_auth) goto noantireplay; if (!sav->alg_auth) goto noantireplay; { u_char authbuf[AH_MAXSUMSIZE]; struct mbuf *n; u_char *p; size_t siz; struct ip *ip; siz = (((*ah_algorithms[sav->alg_auth].sumsiz)(sav) + 3) & ~(4 - 1)); if (AH_MAXSUMSIZE < siz) panic("assertion failed for AH_MAXSUMSIZE"); - if (esp_auth(m, espoff, m->m_pkthdr.len - espoff, sav, authbuf)) - goto noantireplay; + if (esp_auth(m, espoff, m->m_pkthdr.len - espoff, sav, authbuf)) { + ipseclog((LOG_ERR, "ESP checksum generation failure\n")); + m_freem(m); + error = EINVAL; + switch (af) { +#ifdef INET + case AF_INET: + ipsecstat.out_inval++; + break; +#endif +#ifdef INET6 + case AF_INET6: + ipsec6stat.out_inval++; + break; +#endif + } + goto fail; + } n = m; while (n->m_next) n = n->m_next; if (!(n->m_flags & M_EXT) && siz < M_TRAILINGSPACE(n)) { /*XXX*/ n->m_len += siz; m->m_pkthdr.len += siz; p = mtod(n, u_char *) + n->m_len - siz; } else { struct mbuf *nn; MGET(nn, M_DONTWAIT, MT_DATA); if (!nn) { - printf("can't alloc mbuf in esp%d_output", afnumber); + ipseclog((LOG_DEBUG, "can't alloc mbuf in esp%d_output", + afnumber)); m_freem(m); error = ENOBUFS; goto fail; } nn->m_len = siz; nn->m_next = NULL; n->m_next = nn; n = nn; m->m_pkthdr.len += siz; p = mtod(nn, u_char *); } bcopy(authbuf, p, siz); /* modify IP header (for ESP header part only) */ switch (af) { #ifdef INET case AF_INET: ip = mtod(m, struct ip *); if (siz < (IP_MAXPACKET - ntohs(ip->ip_len))) ip->ip_len = htons(ntohs(ip->ip_len) + siz); else { - printf("IPv4 ESP output: size exceeds limit\n"); + ipseclog((LOG_ERR, + "IPv4 ESP output: size exceeds limit\n")); ipsecstat.out_inval++; m_freem(m); error = EMSGSIZE; goto fail; } break; #endif #ifdef INET6 case AF_INET6: /* total packet length will be computed in ip6_output() */ break; #endif } } noantireplay: - if (!m) - printf("NULL mbuf after encryption in esp%d_output", afnumber); - else { + if (!m) { + ipseclog((LOG_ERR, + "NULL mbuf after encryption in esp%d_output", afnumber)); + } else { switch (af) { #ifdef INET case AF_INET: ipsecstat.out_success++; break; #endif #ifdef INET6 case AF_INET6: ipsec6stat.out_success++; break; #endif } } switch (af) { #ifdef INET case AF_INET: ipsecstat.out_esphist[sav->alg_enc]++; break; #endif #ifdef INET6 case AF_INET6: ipsec6stat.out_esphist[sav->alg_enc]++; break; #endif } key_sa_recordxfer(sav, m); return 0; fail: #if 1 return error; #else panic("something bad in esp_output"); #endif } #ifdef INET int esp4_output(m, isr) struct mbuf *m; struct ipsecrequest *isr; { struct ip *ip; if (m->m_len < sizeof(struct ip)) { - printf("esp4_output: first mbuf too short\n"); + ipseclog((LOG_DEBUG, "esp4_output: first mbuf too short\n")); m_freem(m); - return NULL; + return 0; } ip = mtod(m, struct ip *); /* XXX assumes that m->m_next points to payload */ return esp_output(m, &ip->ip_p, m->m_next, isr, AF_INET); } #endif /*INET*/ #ifdef INET6 int esp6_output(m, nexthdrp, md, isr) struct mbuf *m; u_char *nexthdrp; struct mbuf *md; struct ipsecrequest *isr; { if (m->m_len < sizeof(struct ip6_hdr)) { - printf("esp6_output: first mbuf too short\n"); + ipseclog((LOG_DEBUG, "esp6_output: first mbuf too short\n")); m_freem(m); - return NULL; + return 0; } return esp_output(m, nexthdrp, md, isr, AF_INET6); } #endif /*INET6*/ Index: head/sys/netinet6/frag6.c =================================================================== --- head/sys/netinet6/frag6.c (revision 62586) +++ head/sys/netinet6/frag6.c (revision 62587) @@ -1,573 +1,688 @@ +/* $FreeBSD$ */ +/* $KAME: frag6.c,v 1.24 2000/03/25 07:23:41 sumikawa Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include -#include +#include #include /* * Define it to get a correct behavior on per-interface statistics. * You will need to perform an extra routing table lookup, per fragment, * to do it. This may, or may not be, a performance hit. */ -#define IN6_IFSTAT_STRICT +#define IN6_IFSTAT_STRICT -static void frag6_enq __P((struct ip6asfrag *, struct ip6asfrag *)); -static void frag6_deq __P((struct ip6asfrag *)); -static void frag6_insque __P((struct ip6q *, struct ip6q *)); -static void frag6_remque __P((struct ip6q *)); -static void frag6_freef __P((struct ip6q *)); +static void frag6_enq __P((struct ip6asfrag *, struct ip6asfrag *)); +static void frag6_deq __P((struct ip6asfrag *)); +static void frag6_insque __P((struct ip6q *, struct ip6q *)); +static void frag6_remque __P((struct ip6q *)); +static void frag6_freef __P((struct ip6q *)); -int frag6_doing_reass; -u_int frag6_nfragpackets; -struct ip6q ip6q; /* ip6 reassemble queue */ +int frag6_doing_reass; +u_int frag6_nfragpackets; +struct ip6q ip6q; /* ip6 reassemble queue */ -#if !defined(M_FTABLE) +/* FreeBSD tweak */ MALLOC_DEFINE(M_FTABLE, "fragment", "fragment reassembly header"); + +#ifndef offsetof /* XXX */ +#define offsetof(type, member) ((size_t)(&((type *)0)->member)) #endif /* * Initialise reassembly queue and fragment identifier. */ void frag6_init() { struct timeval tv; /* * in many cases, random() here does NOT return random number * as initialization during bootstrap time occur in fixed order. */ microtime(&tv); - ip6q.ip6q_next = ip6q.ip6q_prev = &ip6q; ip6_id = random() ^ tv.tv_usec; + ip6q.ip6q_next = ip6q.ip6q_prev = &ip6q; } /* + * In RFC2460, fragment and reassembly rule do not agree with each other, + * in terms of next header field handling in fragment header. + * While the sender will use the same value for all of the fragmented packets, + * receiver is suggested not to check the consistency. + * + * fragment rule (p20): + * (2) A Fragment header containing: + * The Next Header value that identifies the first header of + * the Fragmentable Part of the original packet. + * -> next header field is same for all fragments + * + * reassembly rule (p21): + * The Next Header field of the last header of the Unfragmentable + * Part is obtained from the Next Header field of the first + * fragment's Fragment header. + * -> should grab it from the first fragment only + * + * The following note also contradicts with fragment rule - noone is going to + * send different fragment with different next header field. + * + * additional note (p22): + * The Next Header values in the Fragment headers of different + * fragments of the same original packet may differ. Only the value + * from the Offset zero fragment packet is used for reassembly. + * -> should grab it from the first fragment only + * + * There is no explicit reason given in the RFC. Historical reason maybe? + */ +/* * Fragment input */ int frag6_input(mp, offp, proto) struct mbuf **mp; int *offp, proto; { struct mbuf *m = *mp, *t; struct ip6_hdr *ip6; struct ip6_frag *ip6f; struct ip6q *q6; - struct ip6asfrag *af6, *ip6af; + struct ip6asfrag *af6, *ip6af, *af6dwn; int offset = *offp, nxt, i, next; int first_frag = 0; - u_short fragoff, frgpartlen; + int fragoff, frgpartlen; /* must be larger than u_int16_t */ struct ifnet *dstifp; #ifdef IN6_IFSTAT_STRICT static struct route_in6 ro; struct sockaddr_in6 *dst; #endif - IP6_EXTHDR_CHECK(m, offset, sizeof(struct ip6_frag), IPPROTO_DONE); - ip6 = mtod(m, struct ip6_hdr *); +#ifndef PULLDOWN_TEST + IP6_EXTHDR_CHECK(m, offset, sizeof(struct ip6_frag), IPPROTO_DONE); ip6f = (struct ip6_frag *)((caddr_t)ip6 + offset); +#else + IP6_EXTHDR_GET(ip6f, struct ip6_frag *, m, offset, sizeof(*ip6f)); + if (ip6f == NULL) + return IPPROTO_DONE; +#endif dstifp = NULL; #ifdef IN6_IFSTAT_STRICT /* find the destination interface of the packet. */ dst = (struct sockaddr_in6 *)&ro.ro_dst; if (ro.ro_rt && ((ro.ro_rt->rt_flags & RTF_UP) == 0 || !IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &ip6->ip6_dst))) { RTFREE(ro.ro_rt); ro.ro_rt = (struct rtentry *)0; } if (ro.ro_rt == NULL) { bzero(dst, sizeof(*dst)); dst->sin6_family = AF_INET6; dst->sin6_len = sizeof(struct sockaddr_in6); dst->sin6_addr = ip6->ip6_dst; } rtalloc((struct route *)&ro); if (ro.ro_rt != NULL && ro.ro_rt->rt_ifa != NULL) dstifp = ((struct in6_ifaddr *)ro.ro_rt->rt_ifa)->ia_ifp; #else /* we are violating the spec, this is not the destination interface */ if ((m->m_flags & M_PKTHDR) != 0) dstifp = m->m_pkthdr.rcvif; #endif /* jumbo payload can't contain a fragment header */ if (ip6->ip6_plen == 0) { icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, offset); in6_ifstat_inc(dstifp, ifs6_reass_fail); return IPPROTO_DONE; } /* * check whether fragment packet's fragment length is * multiple of 8 octets. * sizeof(struct ip6_frag) == 8 * sizeof(struct ip6_hdr) = 40 */ if ((ip6f->ip6f_offlg & IP6F_MORE_FRAG) && (((ntohs(ip6->ip6_plen) - offset) & 0x7) != 0)) { icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, - (caddr_t)&ip6->ip6_plen - (caddr_t)ip6); + offsetof(struct ip6_hdr, ip6_plen)); in6_ifstat_inc(dstifp, ifs6_reass_fail); return IPPROTO_DONE; } ip6stat.ip6s_fragments++; in6_ifstat_inc(dstifp, ifs6_reass_reqd); - /* - * Presence of header sizes in mbufs - * would confuse code below. - */ - + /* offset now points to data portion */ offset += sizeof(struct ip6_frag); - m->m_data += offset; - m->m_len -= offset; for (q6 = ip6q.ip6q_next; q6 != &ip6q; q6 = q6->ip6q_next) if (ip6f->ip6f_ident == q6->ip6q_ident && IN6_ARE_ADDR_EQUAL(&ip6->ip6_src, &q6->ip6q_src) && IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &q6->ip6q_dst)) break; if (q6 == &ip6q) { /* * the first fragment to arrive, create a reassembly queue. */ first_frag = 1; frag6_nfragpackets++; /* * Enforce upper bound on number of fragmented packets * for which we attempt reassembly; * If maxfrag is 0, never accept fragments. * If maxfrag is -1, accept all fragments without limitation. */ if (frag6_nfragpackets >= (u_int)ip6_maxfragpackets) { ip6stat.ip6s_fragoverflow++; in6_ifstat_inc(dstifp, ifs6_reass_fail); frag6_freef(ip6q.ip6q_prev); } q6 = (struct ip6q *)malloc(sizeof(struct ip6q), M_FTABLE, M_DONTWAIT); if (q6 == NULL) goto dropfrag; + bzero(q6, sizeof(*q6)); frag6_insque(q6, &ip6q); + /* ip6q_nxt will be filled afterwards, from 1st fragment */ q6->ip6q_down = q6->ip6q_up = (struct ip6asfrag *)q6; +#ifdef notyet + q6->ip6q_nxtp = (u_char *)nxtp; +#endif q6->ip6q_ident = ip6f->ip6f_ident; q6->ip6q_arrive = 0; /* Is it used anywhere? */ q6->ip6q_ttl = IPV6_FRAGTTL; q6->ip6q_src = ip6->ip6_src; q6->ip6q_dst = ip6->ip6_dst; q6->ip6q_unfrglen = -1; /* The 1st fragment has not arrived. */ } /* * If it's the 1st fragment, record the length of the * unfragmentable part and the next header of the fragment header. */ fragoff = ntohs(ip6f->ip6f_offlg & IP6F_OFF_MASK); if (fragoff == 0) { q6->ip6q_unfrglen = offset - sizeof(struct ip6_hdr) - sizeof(struct ip6_frag); q6->ip6q_nxt = ip6f->ip6f_nxt; } /* * Check that the reassembled packet would not exceed 65535 bytes * in size. * If it would exceed, discard the fragment and return an ICMP error. */ - frgpartlen = sizeof(struct ip6_hdr) + ntohs(ip6->ip6_plen) - offset; + frgpartlen = sizeof(struct ip6_hdr) + ntohs(ip6->ip6_plen) - offset; if (q6->ip6q_unfrglen >= 0) { /* The 1st fragment has already arrived. */ if (q6->ip6q_unfrglen + fragoff + frgpartlen > IPV6_MAXPACKET) { - m->m_data -= offset; - m->m_len += offset; icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, - offset - sizeof(struct ip6_frag) + 2); + offset - sizeof(struct ip6_frag) + + offsetof(struct ip6_frag, ip6f_offlg)); return(IPPROTO_DONE); } } else if (fragoff + frgpartlen > IPV6_MAXPACKET) { - m->m_data -= offset; - m->m_len += offset; icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, - offset - sizeof(struct ip6_frag) + 2); + offset - sizeof(struct ip6_frag) + + offsetof(struct ip6_frag, ip6f_offlg)); return(IPPROTO_DONE); } /* * If it's the first fragment, do the above check for each * fragment already stored in the reassembly queue. */ if (fragoff == 0) { - struct ip6asfrag *af6dwn; - for (af6 = q6->ip6q_down; af6 != (struct ip6asfrag *)q6; af6 = af6dwn) { af6dwn = af6->ip6af_down; if (q6->ip6q_unfrglen + af6->ip6af_off + af6->ip6af_frglen > IPV6_MAXPACKET) { struct mbuf *merr = IP6_REASS_MBUF(af6); struct ip6_hdr *ip6err; int erroff = af6->ip6af_offset; /* dequeue the fragment. */ frag6_deq(af6); + free(af6, M_FTABLE); /* adjust pointer. */ - merr->m_data -= af6->ip6af_offset; - merr->m_len += af6->ip6af_offset; ip6err = mtod(merr, struct ip6_hdr *); /* * Restore source and destination addresses * in the erroneous IPv6 header. */ ip6err->ip6_src = q6->ip6q_src; ip6err->ip6_dst = q6->ip6q_dst; icmp6_error(merr, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, - erroff - sizeof(struct ip6_frag) + 2); + erroff - sizeof(struct ip6_frag) + + offsetof(struct ip6_frag, ip6f_offlg)); } } } - /* Override the IPv6 header */ - ip6af = (struct ip6asfrag *)ip6; + ip6af = (struct ip6asfrag *)malloc(sizeof(struct ip6asfrag), M_FTABLE, + M_DONTWAIT); + if (ip6af == NULL) + goto dropfrag; + bzero(ip6af, sizeof(*ip6af)); + ip6af->ip6af_head = ip6->ip6_flow; + ip6af->ip6af_len = ip6->ip6_plen; + ip6af->ip6af_nxt = ip6->ip6_nxt; + ip6af->ip6af_hlim = ip6->ip6_hlim; ip6af->ip6af_mff = ip6f->ip6f_offlg & IP6F_MORE_FRAG; ip6af->ip6af_off = fragoff; ip6af->ip6af_frglen = frgpartlen; ip6af->ip6af_offset = offset; IP6_REASS_MBUF(ip6af) = m; if (first_frag) { af6 = (struct ip6asfrag *)q6; goto insert; } /* * Find a segment which begins after this one does. */ for (af6 = q6->ip6q_down; af6 != (struct ip6asfrag *)q6; af6 = af6->ip6af_down) if (af6->ip6af_off > ip6af->ip6af_off) break; +#if 0 /* + * If there is a preceding segment, it may provide some of + * our data already. If so, drop the data from the incoming + * segment. If it provides all of our data, drop us. + */ + if (af6->ip6af_up != (struct ip6asfrag *)q6) { + i = af6->ip6af_up->ip6af_off + af6->ip6af_up->ip6af_frglen + - ip6af->ip6af_off; + if (i > 0) { + if (i >= ip6af->ip6af_frglen) + goto dropfrag; + m_adj(IP6_REASS_MBUF(ip6af), i); + ip6af->ip6af_off += i; + ip6af->ip6af_frglen -= i; + } + } + + /* + * While we overlap succeeding segments trim them or, + * if they are completely covered, dequeue them. + */ + while (af6 != (struct ip6asfrag *)q6 && + ip6af->ip6af_off + ip6af->ip6af_frglen > af6->ip6af_off) { + i = (ip6af->ip6af_off + ip6af->ip6af_frglen) - af6->ip6af_off; + if (i < af6->ip6af_frglen) { + af6->ip6af_frglen -= i; + af6->ip6af_off += i; + m_adj(IP6_REASS_MBUF(af6), i); + break; + } + af6 = af6->ip6af_down; + m_freem(IP6_REASS_MBUF(af6->ip6af_up)); + frag6_deq(af6->ip6af_up); + } +#else + /* * If the incoming framgent overlaps some existing fragments in * the reassembly queue, drop it, since it is dangerous to override * existing fragments from a security point of view. */ if (af6->ip6af_up != (struct ip6asfrag *)q6) { i = af6->ip6af_up->ip6af_off + af6->ip6af_up->ip6af_frglen - ip6af->ip6af_off; if (i > 0) { log(LOG_ERR, "%d bytes of a fragment from %s " "overlaps the previous fragment\n", i, ip6_sprintf(&q6->ip6q_src)); goto dropfrag; } } if (af6 != (struct ip6asfrag *)q6) { i = (ip6af->ip6af_off + ip6af->ip6af_frglen) - af6->ip6af_off; if (i > 0) { log(LOG_ERR, "%d bytes of a fragment from %s " "overlaps the succeeding fragment", i, ip6_sprintf(&q6->ip6q_src)); goto dropfrag; } } +#endif insert: /* * Stick new segment in its place; * check for complete reassembly. * Move to front of packet queue, as we are * the most recently active fragmented packet. */ frag6_enq(ip6af, af6->ip6af_up); +#if 0 /* xxx */ + if (q6 != ip6q.ip6q_next) { + frag6_remque(q6); + frag6_insque(q6, &ip6q); + } +#endif next = 0; for (af6 = q6->ip6q_down; af6 != (struct ip6asfrag *)q6; af6 = af6->ip6af_down) { if (af6->ip6af_off != next) { frag6_doing_reass = 0; return IPPROTO_DONE; } next += af6->ip6af_frglen; } if (af6->ip6af_up->ip6af_mff) { frag6_doing_reass = 0; return IPPROTO_DONE; } /* * Reassembly is complete; concatenate fragments. */ - ip6af = q6->ip6q_down; t = m = IP6_REASS_MBUF(ip6af); af6 = ip6af->ip6af_down; + frag6_deq(ip6af); while (af6 != (struct ip6asfrag *)q6) { + af6dwn = af6->ip6af_down; + frag6_deq(af6); while (t->m_next) t = t->m_next; t->m_next = IP6_REASS_MBUF(af6); - af6 = af6->ip6af_down; + m_adj(t->m_next, af6->ip6af_offset); + free(af6, M_FTABLE); + af6 = af6dwn; } /* adjust offset to point where the original next header starts */ offset = ip6af->ip6af_offset - sizeof(struct ip6_frag); - ip6 = (struct ip6_hdr *)ip6af; + free(ip6af, M_FTABLE); + ip6 = mtod(m, struct ip6_hdr *); ip6->ip6_plen = htons((u_short)next + offset - sizeof(struct ip6_hdr)); ip6->ip6_src = q6->ip6q_src; ip6->ip6_dst = q6->ip6q_dst; nxt = q6->ip6q_nxt; +#ifdef notyet + *q6->ip6q_nxtp = (u_char)(nxt & 0xff); +#endif /* * Delete frag6 header with as a few cost as possible. */ - - if (offset < m->m_len) + if (offset < m->m_len) { ovbcopy((caddr_t)ip6, (caddr_t)ip6 + sizeof(struct ip6_frag), offset); - else { - ovbcopy(mtod(m, caddr_t), (caddr_t)ip6 + offset, m->m_len); - m->m_data -= sizeof(struct ip6_frag); + m->m_data += sizeof(struct ip6_frag); + m->m_len -= sizeof(struct ip6_frag); + } else { + /* this comes with no copy if the boundary is on cluster */ + if ((t = m_split(m, offset, M_DONTWAIT)) == NULL) { + frag6_remque(q6); + free(q6, M_FTABLE); + frag6_nfragpackets--; + goto dropfrag; + } + m_adj(t, sizeof(struct ip6_frag)); + m_cat(m, t); } - m->m_data -= offset; - m->m_len += offset; /* * Store NXT to the original. */ { char *prvnxtp = ip6_get_prevhdr(m, offset); /* XXX */ *prvnxtp = nxt; } frag6_remque(q6); free(q6, M_FTABLE); frag6_nfragpackets--; if (m->m_flags & M_PKTHDR) { /* Isn't it always true? */ int plen = 0; for (t = m; t; t = t->m_next) plen += t->m_len; m->m_pkthdr.len = plen; } ip6stat.ip6s_reassembled++; in6_ifstat_inc(dstifp, ifs6_reass_ok); /* * Tell launch routine the next header */ *mp = m; *offp = offset; frag6_doing_reass = 0; return nxt; dropfrag: in6_ifstat_inc(dstifp, ifs6_reass_fail); ip6stat.ip6s_fragdropped++; m_freem(m); return IPPROTO_DONE; } /* * Free a fragment reassembly header and all * associated datagrams. */ void frag6_freef(q6) struct ip6q *q6; { struct ip6asfrag *af6, *down6; for (af6 = q6->ip6q_down; af6 != (struct ip6asfrag *)q6; af6 = down6) { struct mbuf *m = IP6_REASS_MBUF(af6); down6 = af6->ip6af_down; frag6_deq(af6); /* * Return ICMP time exceeded error for the 1st fragment. * Just free other fragments. */ if (af6->ip6af_off == 0) { struct ip6_hdr *ip6; /* adjust pointer */ - m->m_data -= af6->ip6af_offset; - m->m_len += af6->ip6af_offset; ip6 = mtod(m, struct ip6_hdr *); /* restoure source and destination addresses */ ip6->ip6_src = q6->ip6q_src; ip6->ip6_dst = q6->ip6q_dst; icmp6_error(m, ICMP6_TIME_EXCEEDED, ICMP6_TIME_EXCEED_REASSEMBLY, 0); - } - else + } else m_freem(m); + free(af6, M_FTABLE); } frag6_remque(q6); free(q6, M_FTABLE); frag6_nfragpackets--; } /* * Put an ip fragment on a reassembly chain. * Like insque, but pointers in middle of structure. */ void frag6_enq(af6, up6) struct ip6asfrag *af6, *up6; { af6->ip6af_up = up6; af6->ip6af_down = up6->ip6af_down; up6->ip6af_down->ip6af_up = af6; up6->ip6af_down = af6; } /* * To frag6_enq as remque is to insque. */ void frag6_deq(af6) struct ip6asfrag *af6; { af6->ip6af_up->ip6af_down = af6->ip6af_down; af6->ip6af_down->ip6af_up = af6->ip6af_up; } void frag6_insque(new, old) struct ip6q *new, *old; { new->ip6q_prev = old; new->ip6q_next = old->ip6q_next; old->ip6q_next->ip6q_prev= new; old->ip6q_next = new; } void frag6_remque(p6) struct ip6q *p6; { p6->ip6q_prev->ip6q_next = p6->ip6q_next; p6->ip6q_next->ip6q_prev = p6->ip6q_prev; } /* * IP timer processing; * if a timer expires on a reassembly * queue, discard it. */ void frag6_slowtimo() { struct ip6q *q6; int s = splnet(); +#if 0 + extern struct route_in6 ip6_forward_rt; +#endif frag6_doing_reass = 1; q6 = ip6q.ip6q_next; if (q6) while (q6 != &ip6q) { --q6->ip6q_ttl; q6 = q6->ip6q_next; if (q6->ip6q_prev->ip6q_ttl == 0) { ip6stat.ip6s_fragtimeout++; /* XXX in6_ifstat_inc(ifp, ifs6_reass_fail) */ frag6_freef(q6->ip6q_prev); } } /* * If we are over the maximum number of fragments * (due to the limit being lowered), drain off * enough to get down to the new limit. */ while (frag6_nfragpackets > (u_int)ip6_maxfragpackets) { ip6stat.ip6s_fragoverflow++; /* XXX in6_ifstat_inc(ifp, ifs6_reass_fail) */ frag6_freef(ip6q.ip6q_prev); } frag6_doing_reass = 0; + +#if 0 + /* + * Routing changes might produce a better route than we last used; + * make sure we notice eventually, even if forwarding only for one + * destination and the cache is never replaced. + */ + if (ip6_forward_rt.ro_rt) { + RTFREE(ip6_forward_rt.ro_rt); + ip6_forward_rt.ro_rt = 0; + } + if (ipsrcchk_rt.ro_rt) { + RTFREE(ipsrcchk_rt.ro_rt); + ipsrcchk_rt.ro_rt = 0; + } +#endif + splx(s); } /* * Drain off all datagram fragments. */ void frag6_drain() { if (frag6_doing_reass) return; while (ip6q.ip6q_next != &ip6q) { ip6stat.ip6s_fragdropped++; /* XXX in6_ifstat_inc(ifp, ifs6_reass_fail) */ frag6_freef(ip6q.ip6q_next); } } Index: head/sys/netinet6/icmp6.c =================================================================== --- head/sys/netinet6/icmp6.c (revision 62586) +++ head/sys/netinet6/icmp6.c (revision 62587) @@ -1,1854 +1,2606 @@ +/* $FreeBSD$ */ +/* $KAME: icmp6.c,v 1.119 2000/07/03 14:16:46 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* * Copyright (c) 1982, 1986, 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)ip_icmp.c 8.2 (Berkeley) 1/4/94 */ +#include "opt_inet.h" +#include "opt_inet6.h" #include "opt_ipsec.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include -#include +#include #include #include +#include #include #include #include #ifdef IPSEC #include -#include +#ifdef INET6 #include -#include +#endif #include -#ifdef IPSEC_DEBUG -#include -#else -#define KEYDEBUG(lev,arg) #endif -#endif /* IPSEC */ #include "faith.h" #include -extern struct domain inet6domain; -extern struct ip6protosw inet6sw[]; -extern u_char ip6_protox[]; +extern struct domain inet6domain; +extern struct ip6protosw inet6sw[]; +extern u_char ip6_protox[]; -struct icmp6stat icmp6stat; +struct icmp6stat icmp6stat; -extern struct inpcbhead ripcb; -extern u_int icmp6errratelim; +extern struct inpcbhead ripcb; +extern struct timeval icmp6errratelim; +static struct timeval icmp6errratelim_last; +extern int icmp6errppslim; +static int icmp6errpps_count = 0; +extern int icmp6_nodeinfo; -static int icmp6_rip6_input __P((struct mbuf **, int)); -static int icmp6_ratelimit __P((const struct in6_addr *, const int, const int)); -static const char *icmp6_redirect_diag __P((struct in6_addr *, - struct in6_addr *, - struct in6_addr *)); -static struct mbuf *ni6_input __P((struct mbuf *, int)); -static int ni6_addrs __P((struct icmp6_nodeinfo *, struct mbuf *, - struct ifnet **)); -static int ni6_store_addrs __P((struct icmp6_nodeinfo *, - struct icmp6_nodeinfo *, - struct ifnet *, int)); +static void icmp6_errcount __P((struct icmp6errstat *, int, int)); +static int icmp6_rip6_input __P((struct mbuf **, int)); +static void icmp6_mtudisc_update __P((struct in6_addr *, struct icmp6_hdr *, + struct mbuf *)); +static int icmp6_ratelimit __P((const struct in6_addr *, const int, const int)); +static const char *icmp6_redirect_diag __P((struct in6_addr *, + struct in6_addr *, struct in6_addr *)); +#ifndef HAVE_RATECHECK +static int ratecheck __P((struct timeval *, struct timeval *)); +#endif +static struct mbuf *ni6_input __P((struct mbuf *, int)); +static struct mbuf *ni6_nametodns __P((const char *, int, int)); +static int ni6_dnsmatch __P((const char *, int, const char *, int)); +static int ni6_addrs __P((struct icmp6_nodeinfo *, struct mbuf *, + struct ifnet **)); +static int ni6_store_addrs __P((struct icmp6_nodeinfo *, struct icmp6_nodeinfo *, + struct ifnet *, int)); #ifdef COMPAT_RFC1885 -static struct route_in6 icmp6_reflect_rt; +static struct route_in6 icmp6_reflect_rt; #endif -static struct timeval icmp6_nextsend = {0, 0}; void icmp6_init() { mld6_init(); } +static void +icmp6_errcount(stat, type, code) + struct icmp6errstat *stat; + int type, code; +{ + switch(type) { + case ICMP6_DST_UNREACH: + switch (code) { + case ICMP6_DST_UNREACH_NOROUTE: + stat->icp6errs_dst_unreach_noroute++; + return; + case ICMP6_DST_UNREACH_ADMIN: + stat->icp6errs_dst_unreach_admin++; + return; + case ICMP6_DST_UNREACH_BEYONDSCOPE: + stat->icp6errs_dst_unreach_beyondscope++; + return; + case ICMP6_DST_UNREACH_ADDR: + stat->icp6errs_dst_unreach_addr++; + return; + case ICMP6_DST_UNREACH_NOPORT: + stat->icp6errs_dst_unreach_noport++; + return; + } + break; + case ICMP6_PACKET_TOO_BIG: + stat->icp6errs_packet_too_big++; + return; + case ICMP6_TIME_EXCEEDED: + switch(code) { + case ICMP6_TIME_EXCEED_TRANSIT: + stat->icp6errs_time_exceed_transit++; + return; + case ICMP6_TIME_EXCEED_REASSEMBLY: + stat->icp6errs_time_exceed_reassembly++; + return; + } + break; + case ICMP6_PARAM_PROB: + switch(code) { + case ICMP6_PARAMPROB_HEADER: + stat->icp6errs_paramprob_header++; + return; + case ICMP6_PARAMPROB_NEXTHEADER: + stat->icp6errs_paramprob_nextheader++; + return; + case ICMP6_PARAMPROB_OPTION: + stat->icp6errs_paramprob_option++; + return; + } + break; + case ND_REDIRECT: + stat->icp6errs_redirect++; + return; + } + stat->icp6errs_unknown++; +} + /* * Generate an error packet of type error in response to bad IP6 packet. */ void icmp6_error(m, type, code, param) struct mbuf *m; int type, code, param; { struct ip6_hdr *oip6, *nip6; struct icmp6_hdr *icmp6; - u_int prep; + u_int preplen; int off; - u_char nxt; + int nxt; icmp6stat.icp6s_error++; - if (m->m_flags & M_DECRYPTED) + /* count per-type-code statistics */ + icmp6_errcount(&icmp6stat.icp6s_outerrhist, type, code); + +#ifdef M_DECRYPTED /*not openbsd*/ + if (m->m_flags & M_DECRYPTED) { + icmp6stat.icp6s_canterror++; goto freeit; + } +#endif +#ifndef PULLDOWN_TEST + IP6_EXTHDR_CHECK(m, 0, sizeof(struct ip6_hdr), ); +#else + if (m->m_len < sizeof(struct ip6_hdr)) { + m = m_pullup(m, sizeof(struct ip6_hdr)); + if (m == NULL) + return; + } +#endif oip6 = mtod(m, struct ip6_hdr *); /* * Multicast destination check. For unrecognized option errors, * this check has already done in ip6_unknown_opt(), so we can * check only for other errors. */ if ((m->m_flags & (M_BCAST|M_MCAST) || IN6_IS_ADDR_MULTICAST(&oip6->ip6_dst)) && (type != ICMP6_PACKET_TOO_BIG && (type != ICMP6_PARAM_PROB || code != ICMP6_PARAMPROB_OPTION))) goto freeit; /* Source address check. XXX: the case of anycast source? */ if (IN6_IS_ADDR_UNSPECIFIED(&oip6->ip6_src) || IN6_IS_ADDR_MULTICAST(&oip6->ip6_src)) goto freeit; /* - * If the erroneous packet is also an ICMP error, discard it. + * If we are about to send ICMPv6 against ICMPv6 error/redirect, + * don't do it. */ - IP6_EXTHDR_CHECK(m, 0, sizeof(struct ip6_hdr), ); - off = sizeof(struct ip6_hdr); - nxt = oip6->ip6_nxt; - while(1) { /* XXX: should avoid inf. loop explicitly? */ - struct ip6_ext *ip6e; + nxt = -1; + off = ip6_lasthdr(m, 0, IPPROTO_IPV6, &nxt); + if (off >= 0 && nxt == IPPROTO_ICMPV6) { struct icmp6_hdr *icp; - switch(nxt) { - case IPPROTO_IPV6: - case IPPROTO_IPV4: - case IPPROTO_UDP: - case IPPROTO_TCP: - case IPPROTO_ESP: - case IPPROTO_FRAGMENT: +#ifndef PULLDOWN_TEST + IP6_EXTHDR_CHECK(m, 0, off + sizeof(struct icmp6_hdr), ); + icp = (struct icmp6_hdr *)(mtod(m, caddr_t) + off); +#else + IP6_EXTHDR_GET(icp, struct icmp6_hdr *, m, off, + sizeof(*icp)); + if (icp == NULL) { + icmp6stat.icp6s_tooshort++; + return; + } +#endif + if (icp->icmp6_type < ICMP6_ECHO_REQUEST || + icp->icmp6_type == ND_REDIRECT) { /* - * ICMPv6 error must not be fragmented. - * XXX: but can we trust the sender? + * ICMPv6 error + * Special case: for redirect (which is + * informational) we must not send icmp6 error. */ - default: - /* What if unknown header followed by ICMP error? */ - goto generate; - case IPPROTO_ICMPV6: - IP6_EXTHDR_CHECK(m, 0, off + sizeof(struct icmp6_hdr), ); - icp = (struct icmp6_hdr *)(mtod(m, caddr_t) + off); - if (icp->icmp6_type < ICMP6_ECHO_REQUEST - || icp->icmp6_type == ND_REDIRECT) { - /* - * ICMPv6 error - * Special case: for redirect (which is - * informational) we must not send icmp6 error. - */ - icmp6stat.icp6s_canterror++; - goto freeit; - } else { - /* ICMPv6 informational */ - goto generate; - } - case IPPROTO_HOPOPTS: - case IPPROTO_DSTOPTS: - case IPPROTO_ROUTING: - case IPPROTO_AH: - IP6_EXTHDR_CHECK(m, 0, off + sizeof(struct ip6_ext), ); - ip6e = (struct ip6_ext *)(mtod(m, caddr_t) + off); - if (nxt == IPPROTO_AH) - off += (ip6e->ip6e_len + 2) << 2; - else - off += (ip6e->ip6e_len + 1) << 3; - nxt = ip6e->ip6e_nxt; - break; + icmp6stat.icp6s_canterror++; + goto freeit; + } else { + /* ICMPv6 informational - send the error */ } + } else { + /* non-ICMPv6 - send the error */ } - freeit: - /* - * If we can't tell wheter or not we can generate ICMP6, free it. - */ - m_freem(m); - return; - - generate: oip6 = mtod(m, struct ip6_hdr *); /* adjust pointer */ /* Finally, do rate limitation check. */ if (icmp6_ratelimit(&oip6->ip6_src, type, code)) { icmp6stat.icp6s_toofreq++; goto freeit; } /* * OK, ICMP6 can be generated. */ if (m->m_pkthdr.len >= ICMPV6_PLD_MAXLEN) m_adj(m, ICMPV6_PLD_MAXLEN - m->m_pkthdr.len); - prep = sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr); - M_PREPEND(m, prep, M_DONTWAIT); - if (m && m->m_len < prep) - m = m_pullup(m, prep); + preplen = sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr); + M_PREPEND(m, preplen, M_DONTWAIT); + if (m && m->m_len < preplen) + m = m_pullup(m, preplen); if (m == NULL) { printf("ENOBUFS in icmp6_error %d\n", __LINE__); return; } nip6 = mtod(m, struct ip6_hdr *); nip6->ip6_src = oip6->ip6_src; nip6->ip6_dst = oip6->ip6_dst; if (IN6_IS_SCOPE_LINKLOCAL(&oip6->ip6_src)) oip6->ip6_src.s6_addr16[1] = 0; if (IN6_IS_SCOPE_LINKLOCAL(&oip6->ip6_dst)) oip6->ip6_dst.s6_addr16[1] = 0; icmp6 = (struct icmp6_hdr *)(nip6 + 1); icmp6->icmp6_type = type; icmp6->icmp6_code = code; icmp6->icmp6_pptr = htonl((u_int32_t)param); icmp6stat.icp6s_outhist[type]++; icmp6_reflect(m, sizeof(struct ip6_hdr)); /*header order: IPv6 - ICMPv6*/ + + return; + + freeit: + /* + * If we can't tell wheter or not we can generate ICMP6, free it. + */ + m_freem(m); } /* * Process a received ICMP6 message. */ int icmp6_input(mp, offp, proto) struct mbuf **mp; int *offp, proto; { struct mbuf *m = *mp, *n; struct ip6_hdr *ip6, *nip6; struct icmp6_hdr *icmp6, *nicmp6; int off = *offp; int icmp6len = m->m_pkthdr.len - *offp; int code, sum, noff; struct sockaddr_in6 icmp6src; +#ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, off, sizeof(struct icmp6_hdr), IPPROTO_DONE); /* m might change if M_LOOP. So, call mtod after this */ +#endif /* * Locate icmp6 structure in mbuf, and check * that not corrupted and of at least minimum length */ ip6 = mtod(m, struct ip6_hdr *); if (icmp6len < sizeof(struct icmp6_hdr)) { icmp6stat.icp6s_tooshort++; goto freeit; } /* * calculate the checksum */ - +#ifndef PULLDOWN_TEST icmp6 = (struct icmp6_hdr *)((caddr_t)ip6 + off); +#else + IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off, sizeof(*icmp6)); + if (icmp6 == NULL) { + icmp6stat.icp6s_tooshort++; + return IPPROTO_DONE; + } +#endif code = icmp6->icmp6_code; if ((sum = in6_cksum(m, IPPROTO_ICMPV6, off, icmp6len)) != 0) { log(LOG_ERR, "ICMP6 checksum error(%d|%x) %s\n", icmp6->icmp6_type, sum, ip6_sprintf(&ip6->ip6_src)); icmp6stat.icp6s_checksum++; goto freeit; } #if defined(NFAITH) && 0 < NFAITH if (m->m_pkthdr.rcvif && m->m_pkthdr.rcvif->if_type == IFT_FAITH) { /* * Deliver very specific ICMP6 type only. * This is important to deilver TOOBIG. Otherwise PMTUD * will not work. */ switch (icmp6->icmp6_type) { case ICMP6_DST_UNREACH: case ICMP6_PACKET_TOO_BIG: case ICMP6_TIME_EXCEEDED: break; default: goto freeit; } } #endif #ifdef IPSEC /* drop it if it does not match the default policy */ if (ipsec6_in_reject(m, NULL)) { ipsecstat.in_polvio++; goto freeit; } #endif icmp6stat.icp6s_inhist[icmp6->icmp6_type]++; icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_msg); if (icmp6->icmp6_type < ICMP6_INFOMSG_MASK) icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_error); switch (icmp6->icmp6_type) { case ICMP6_DST_UNREACH: icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_dstunreach); switch (code) { case ICMP6_DST_UNREACH_NOROUTE: code = PRC_UNREACH_NET; break; case ICMP6_DST_UNREACH_ADMIN: icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_adminprohib); + code = PRC_UNREACH_PROTOCOL; /* is this a good code? */ + break; case ICMP6_DST_UNREACH_ADDR: - code = PRC_UNREACH_HOST; + code = PRC_HOSTDEAD; break; +#ifdef COMPAT_RFC1885 case ICMP6_DST_UNREACH_NOTNEIGHBOR: code = PRC_UNREACH_SRCFAIL; break; +#else + case ICMP6_DST_UNREACH_BEYONDSCOPE: + /* I mean "source address was incorrect." */ + code = PRC_PARAMPROB; + break; +#endif case ICMP6_DST_UNREACH_NOPORT: code = PRC_UNREACH_PORT; break; default: goto badcode; } goto deliver; break; case ICMP6_PACKET_TOO_BIG: icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_pkttoobig); if (code != 0) goto badcode; - { - u_int mtu = ntohl(icmp6->icmp6_mtu); - struct rtentry *rt = NULL; - struct sockaddr_in6 sin6; code = PRC_MSGSIZE; - bzero(&sin6, sizeof(sin6)); - sin6.sin6_family = PF_INET6; - sin6.sin6_len = sizeof(struct sockaddr_in6); - sin6.sin6_addr = ((struct ip6_hdr *)(icmp6 + 1))->ip6_dst; - rt = rtalloc1((struct sockaddr *)&sin6, 0, - RTF_CLONING | RTF_PRCLONING); - if (rt && (rt->rt_flags & RTF_HOST) - && !(rt->rt_rmx.rmx_locks & RTV_MTU)) { - if (mtu < IPV6_MMTU) { - /* xxx */ - rt->rt_rmx.rmx_locks |= RTV_MTU; - } else if (mtu < rt->rt_ifp->if_mtu && - rt->rt_rmx.rmx_mtu > mtu) { - rt->rt_rmx.rmx_mtu = mtu; - } - } - if (rt) - RTFREE(rt); + /* + * Updating the path MTU will be done after examining + * intermediate extension headers. + */ goto deliver; - } break; case ICMP6_TIME_EXCEEDED: icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_timeexceed); switch (code) { case ICMP6_TIME_EXCEED_TRANSIT: case ICMP6_TIME_EXCEED_REASSEMBLY: code += PRC_TIMXCEED_INTRANS; break; default: goto badcode; } goto deliver; break; case ICMP6_PARAM_PROB: icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_paramprob); switch (code) { case ICMP6_PARAMPROB_NEXTHEADER: code = PRC_UNREACH_PROTOCOL; break; case ICMP6_PARAMPROB_HEADER: case ICMP6_PARAMPROB_OPTION: code = PRC_PARAMPROB; break; default: goto badcode; } goto deliver; break; case ICMP6_ECHO_REQUEST: icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_echo); if (code != 0) goto badcode; if ((n = m_copy(m, 0, M_COPYALL)) == NULL) { /* Give up remote */ break; } - if (n->m_flags & M_EXT) { - int gap, move; + if ((n->m_flags & M_EXT) != 0 + || n->m_len < off + sizeof(struct icmp6_hdr)) { struct mbuf *n0 = n; + const int maxlen = sizeof(*nip6) + sizeof(*nicmp6); /* * Prepare an internal mbuf. m_pullup() doesn't * always copy the length we specified. */ + if (maxlen >= MCLBYTES) { +#ifdef DIAGNOSTIC + printf("MCLBYTES too small\n"); +#endif + /* Give up remote */ + m_freem(n0); + break; + } MGETHDR(n, M_DONTWAIT, n0->m_type); + if (n && maxlen >= MHLEN) { + MCLGET(n, M_DONTWAIT); + if ((n->m_flags & M_EXT) == 0) { + m_free(n); + n = NULL; + } + } if (n == NULL) { /* Give up remote */ m_freem(n0); break; } M_COPY_PKTHDR(n, n0); - n0->m_flags &= ~M_PKTHDR; - n->m_next = n0; /* * Copy IPv6 and ICMPv6 only. */ nip6 = mtod(n, struct ip6_hdr *); bcopy(ip6, nip6, sizeof(struct ip6_hdr)); nicmp6 = (struct icmp6_hdr *)(nip6 + 1); bcopy(icmp6, nicmp6, sizeof(struct icmp6_hdr)); + noff = sizeof(struct ip6_hdr); + n->m_pkthdr.len = n->m_len = + noff + sizeof(struct icmp6_hdr); /* - * Adjust mbuf. ip6_plen will be adjusted. + * Adjust mbuf. ip6_plen will be adjusted in + * ip6_output(). */ - noff = sizeof(struct ip6_hdr); - n->m_len = noff + sizeof(struct icmp6_hdr); - move = off + sizeof(struct icmp6_hdr); - n0->m_len -= move; - n0->m_data += move; - gap = off - noff; - n->m_pkthdr.len -= gap; + m_adj(n0, off + sizeof(struct icmp6_hdr)); + n->m_pkthdr.len += n0->m_pkthdr.len; + n->m_next = n0; + n0->m_flags &= ~M_PKTHDR; } else { nip6 = mtod(n, struct ip6_hdr *); nicmp6 = (struct icmp6_hdr *)((caddr_t)nip6 + off); noff = off; } nicmp6->icmp6_type = ICMP6_ECHO_REPLY; nicmp6->icmp6_code = 0; if (n) { icmp6stat.icp6s_reflect++; icmp6stat.icp6s_outhist[ICMP6_ECHO_REPLY]++; icmp6_reflect(n, noff); } break; case ICMP6_ECHO_REPLY: icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_echoreply); if (code != 0) goto badcode; break; case MLD6_LISTENER_QUERY: case MLD6_LISTENER_REPORT: if (icmp6len < sizeof(struct mld6_hdr)) goto badlen; if (icmp6->icmp6_type == MLD6_LISTENER_QUERY) /* XXX: ugly... */ icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_mldquery); else icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_mldreport); - IP6_EXTHDR_CHECK(m, off, icmp6len, IPPROTO_DONE); - mld6_input(m, off); + if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) { + /* give up local */ + mld6_input(m, off); + m = NULL; + goto freeit; + } + mld6_input(n, off); /* m stays. */ break; case MLD6_LISTENER_DONE: icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_mlddone); if (icmp6len < sizeof(struct mld6_hdr)) /* necessary? */ goto badlen; break; /* nothing to be done in kernel */ case MLD6_MTRACE_RESP: case MLD6_MTRACE: /* XXX: these two are experimental. not officially defind. */ /* XXX: per-interface statistics? */ - break; /* just pass it to the userland daemon */ + break; /* just pass it to applications */ case ICMP6_WRUREQUEST: /* ICMP6_FQDN_QUERY */ { enum { WRU, FQDN } mode; - if (code != 0) - goto badcode; + if (!icmp6_nodeinfo) + break; + if (icmp6len == sizeof(struct icmp6_hdr) + 4) mode = WRU; - else if (icmp6len >= sizeof(struct icmp6_hdr) + 8) /* XXX */ + else if (icmp6len >= sizeof(struct icmp6_nodeinfo)) mode = FQDN; else goto badlen; #define hostnamelen strlen(hostname) if (mode == FQDN) { +#ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, off, sizeof(struct icmp6_nodeinfo), IPPROTO_DONE); - n = ni6_input(m, off); +#endif + n = m_copy(m, 0, M_COPYALL); + if (n) + n = ni6_input(n, off); + /* XXX meaningless if n == NULL */ noff = sizeof(struct ip6_hdr); } else { u_char *p; + int maxlen, maxhlen; + if (code != 0) + goto badcode; + maxlen = sizeof(*nip6) + sizeof(*nicmp6) + 4; + if (maxlen >= MCLBYTES) { +#ifdef DIAGNOSTIC + printf("MCLBYTES too small\n"); +#endif + /* Give up remote */ + break; + } MGETHDR(n, M_DONTWAIT, m->m_type); + if (n && maxlen > MHLEN) { + MCLGET(n, M_DONTWAIT); + if ((n->m_flags & M_EXT) == 0) { + m_free(n); + n = NULL; + } + } if (n == NULL) { /* Give up remote */ break; } + n->m_len = 0; + maxhlen = M_TRAILINGSPACE(n) - maxlen; + if (maxhlen > hostnamelen) + maxhlen = hostnamelen; /* * Copy IPv6 and ICMPv6 only. */ nip6 = mtod(n, struct ip6_hdr *); bcopy(ip6, nip6, sizeof(struct ip6_hdr)); nicmp6 = (struct icmp6_hdr *)(nip6 + 1); bcopy(icmp6, nicmp6, sizeof(struct icmp6_hdr)); p = (u_char *)(nicmp6 + 1); bzero(p, 4); - bcopy(hostname, p + 4, hostnamelen); + bcopy(hostname, p + 4, maxhlen); /*meaningless TTL*/ noff = sizeof(struct ip6_hdr); M_COPY_PKTHDR(n, m); /* just for recvif */ n->m_pkthdr.len = n->m_len = sizeof(struct ip6_hdr) + - sizeof(struct icmp6_hdr) + 4 + hostnamelen; + sizeof(struct icmp6_hdr) + 4 + maxhlen; nicmp6->icmp6_type = ICMP6_WRUREPLY; nicmp6->icmp6_code = 0; } #undef hostnamelen if (n) { icmp6stat.icp6s_reflect++; icmp6stat.icp6s_outhist[ICMP6_WRUREPLY]++; icmp6_reflect(n, noff); } break; } case ICMP6_WRUREPLY: if (code != 0) goto badcode; break; case ND_ROUTER_SOLICIT: icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_routersolicit); if (code != 0) goto badcode; if (icmp6len < sizeof(struct nd_router_solicit)) goto badlen; - IP6_EXTHDR_CHECK(m, off, icmp6len, IPPROTO_DONE); - nd6_rs_input(m, off, icmp6len); + if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) { + /* give up local */ + nd6_rs_input(m, off, icmp6len); + m = NULL; + goto freeit; + } + nd6_rs_input(n, off, icmp6len); /* m stays. */ break; case ND_ROUTER_ADVERT: icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_routeradvert); if (code != 0) goto badcode; if (icmp6len < sizeof(struct nd_router_advert)) goto badlen; - IP6_EXTHDR_CHECK(m, off, icmp6len, IPPROTO_DONE); - nd6_ra_input(m, off, icmp6len); + if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) { + /* give up local */ + nd6_ra_input(m, off, icmp6len); + m = NULL; + goto freeit; + } + nd6_ra_input(n, off, icmp6len); /* m stays. */ break; case ND_NEIGHBOR_SOLICIT: icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_neighborsolicit); if (code != 0) goto badcode; if (icmp6len < sizeof(struct nd_neighbor_solicit)) goto badlen; - IP6_EXTHDR_CHECK(m, off, icmp6len, IPPROTO_DONE); - nd6_ns_input(m, off, icmp6len); + if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) { + /* give up local */ + nd6_ns_input(m, off, icmp6len); + m = NULL; + goto freeit; + } + nd6_ns_input(n, off, icmp6len); /* m stays. */ break; case ND_NEIGHBOR_ADVERT: icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_neighboradvert); if (code != 0) goto badcode; if (icmp6len < sizeof(struct nd_neighbor_advert)) goto badlen; - IP6_EXTHDR_CHECK(m, off, icmp6len, IPPROTO_DONE); - nd6_na_input(m, off, icmp6len); + if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) { + /* give up local */ + nd6_na_input(m, off, icmp6len); + m = NULL; + goto freeit; + } + nd6_na_input(n, off, icmp6len); /* m stays. */ break; case ND_REDIRECT: icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_redirect); if (code != 0) goto badcode; if (icmp6len < sizeof(struct nd_redirect)) goto badlen; - icmp6_redirect_input(m, off); + if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) { + /* give up local */ + icmp6_redirect_input(m, off); + m = NULL; + goto freeit; + } + icmp6_redirect_input(n, off); /* m stays. */ break; case ICMP6_ROUTER_RENUMBERING: if (code != ICMP6_ROUTER_RENUMBERING_COMMAND && code != ICMP6_ROUTER_RENUMBERING_RESULT) goto badcode; if (icmp6len < sizeof(struct icmp6_router_renum)) goto badlen; break; default: printf("icmp6_input: unknown type %d(src=%s, dst=%s, ifid=%d)\n", icmp6->icmp6_type, ip6_sprintf(&ip6->ip6_src), ip6_sprintf(&ip6->ip6_dst), m->m_pkthdr.rcvif ? m->m_pkthdr.rcvif->if_index : 0); if (icmp6->icmp6_type < ICMP6_ECHO_REQUEST) { /* ICMPv6 error: MUST deliver it by spec... */ code = PRC_NCMDS; /* deliver */ } else { /* ICMPv6 informational: MUST not deliver */ break; } deliver: if (icmp6len < sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr)) { icmp6stat.icp6s_tooshort++; goto freeit; } +#ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, off, sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr), IPPROTO_DONE); icmp6 = (struct icmp6_hdr *)(mtod(m, caddr_t) + off); +#else + IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off, + sizeof(*icmp6) + sizeof(struct ip6_hdr)); + if (icmp6 == NULL) { + icmp6stat.icp6s_tooshort++; + return IPPROTO_DONE; + } +#endif bzero(&icmp6src, sizeof(icmp6src)); icmp6src.sin6_len = sizeof(struct sockaddr_in6); icmp6src.sin6_family = AF_INET6; icmp6src.sin6_addr = ((struct ip6_hdr *)(icmp6 + 1))->ip6_dst; /* Detect the upper level protocol */ { void (*ctlfunc) __P((int, struct sockaddr *, void *)); struct ip6_hdr *eip6 = (struct ip6_hdr *)(icmp6 + 1); u_int8_t nxt = eip6->ip6_nxt; int eoff = off + sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr); struct ip6ctlparam ip6cp; + struct in6_addr *finaldst = NULL; + int icmp6type = icmp6->icmp6_type; + struct ip6_frag *fh; + struct ip6_rthdr *rth; + struct ip6_rthdr0 *rth0; + int rthlen; while (1) { /* XXX: should avoid inf. loop explicitly? */ struct ip6_ext *eh; switch(nxt) { - case IPPROTO_ESP: - case IPPROTO_NONE: - goto passit; case IPPROTO_HOPOPTS: case IPPROTO_DSTOPTS: - case IPPROTO_ROUTING: case IPPROTO_AH: - case IPPROTO_FRAGMENT: +#ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, 0, eoff + sizeof(struct ip6_ext), IPPROTO_DONE); eh = (struct ip6_ext *)(mtod(m, caddr_t) + eoff); +#else + IP6_EXTHDR_GET(eh, struct ip6_ext *, m, + eoff, sizeof(*eh)); + if (eh == NULL) { + icmp6stat.icp6s_tooshort++; + return IPPROTO_DONE; + } +#endif + if (nxt == IPPROTO_AH) eoff += (eh->ip6e_len + 2) << 2; - else if (nxt == IPPROTO_FRAGMENT) - eoff += sizeof(struct ip6_frag); else eoff += (eh->ip6e_len + 1) << 3; nxt = eh->ip6e_nxt; break; + case IPPROTO_ROUTING: + /* + * When the erroneous packet contains a + * routing header, we should examine the + * header to determine the final destination. + * Otherwise, we can't properly update + * information that depends on the final + * destination (e.g. path MTU). + */ +#ifndef PULLDOWN_TEST + IP6_EXTHDR_CHECK(m, 0, eoff + sizeof(*rth), + IPPROTO_DONE); + rth = (struct ip6_rthdr *)(mtod(m, caddr_t) + + eoff); +#else + IP6_EXTHDR_GET(rth, struct ip6_rthdr *, m, + eoff, sizeof(*rth)); + if (rth == NULL) { + icmp6stat.icp6s_tooshort++; + return IPPROTO_DONE; + } +#endif + rthlen = (rth->ip6r_len + 1) << 3; + /* + * XXX: currently there is no + * officially defined type other + * than type-0. + * Note that if the segment left field + * is 0, all intermediate hops must + * have been passed. + */ + if (rth->ip6r_segleft && + rth->ip6r_type == IPV6_RTHDR_TYPE_0) { + int hops; + +#ifndef PULLDOWN_TEST + IP6_EXTHDR_CHECK(m, 0, eoff + rthlen, + IPPROTO_DONE); + rth0 = (struct ip6_rthdr0 *)(mtod(m, caddr_t) + eoff); +#else + IP6_EXTHDR_GET(rth0, + struct ip6_rthdr0 *, m, + eoff, rthlen); + if (rth0 == NULL) { + icmp6stat.icp6s_tooshort++; + return IPPROTO_DONE; + } +#endif + /* just ignore a bogus header */ + if ((rth0->ip6r0_len % 2) == 0 && + (hops = rth0->ip6r0_len/2)) + finaldst = (struct in6_addr *)(rth0 + 1) + (hops - 1); + } + eoff += rthlen; + nxt = rth->ip6r_nxt; + break; + case IPPROTO_FRAGMENT: +#ifndef PULLDOWN_TEST + IP6_EXTHDR_CHECK(m, 0, eoff + + sizeof(struct ip6_frag), + IPPROTO_DONE); + fh = (struct ip6_frag *)(mtod(m, caddr_t) + + eoff); +#else + IP6_EXTHDR_GET(fh, struct ip6_frag *, m, + eoff, sizeof(*fh)); + if (fh == NULL) { + icmp6stat.icp6s_tooshort++; + return IPPROTO_DONE; + } +#endif + /* + * Data after a fragment header is meaningless + * unless it is the first fragment, but + * we'll go to the notify label for path MTU + * discovery. + */ + if (fh->ip6f_offlg & IP6F_OFF_MASK) + goto notify; + + eoff += sizeof(struct ip6_frag); + nxt = fh->ip6f_nxt; + break; default: + /* + * This case includes ESP and the No Next + * Header. In such cases going to the notify + * label does not have any meaning + * (i.e. ctlfunc will be NULL), but we go + * anyway since we might have to update + * path MTU information. + */ goto notify; } } notify: +#ifndef PULLDOWN_TEST icmp6 = (struct icmp6_hdr *)(mtod(m, caddr_t) + off); +#else + IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off, + sizeof(*icmp6) + sizeof(struct ip6_hdr)); + if (icmp6 == NULL) { + icmp6stat.icp6s_tooshort++; + return IPPROTO_DONE; + } +#endif + if (icmp6type == ICMP6_PACKET_TOO_BIG) { + if (finaldst == NULL) + finaldst = &((struct ip6_hdr *)(icmp6 + 1))->ip6_dst; + icmp6_mtudisc_update(finaldst, icmp6, m); + } + ctlfunc = (void (*) __P((int, struct sockaddr *, void *))) (inet6sw[ip6_protox[nxt]].pr_ctlinput); if (ctlfunc) { ip6cp.ip6c_m = m; ip6cp.ip6c_ip6 = (struct ip6_hdr *)(icmp6 + 1); ip6cp.ip6c_off = eoff; (*ctlfunc)(code, (struct sockaddr *)&icmp6src, &ip6cp); } } break; badcode: icmp6stat.icp6s_badcode++; break; badlen: icmp6stat.icp6s_badlen++; break; } - passit: +#ifdef HAVE_NRL_INPCB + rip6_input(&m, offp, IPPROTO_ICMPV6); +#else icmp6_rip6_input(&m, *offp); +#endif return IPPROTO_DONE; freeit: m_freem(m); return IPPROTO_DONE; } +static void +icmp6_mtudisc_update(dst, icmp6, m) + struct in6_addr *dst; + struct icmp6_hdr *icmp6;/* we can assume the validity of the pointer */ + struct mbuf *m; /* currently unused but added for scoped addrs */ +{ + u_int mtu = ntohl(icmp6->icmp6_mtu); + struct rtentry *rt = NULL; + struct sockaddr_in6 sin6; + + bzero(&sin6, sizeof(sin6)); + sin6.sin6_family = PF_INET6; + sin6.sin6_len = sizeof(struct sockaddr_in6); + sin6.sin6_addr = *dst; + /* sin6.sin6_scope_id = XXX: should be set if DST is a scoped addr */ + rt = rtalloc1((struct sockaddr *)&sin6, 0, + RTF_CLONING | RTF_PRCLONING); + + if (rt && (rt->rt_flags & RTF_HOST) + && !(rt->rt_rmx.rmx_locks & RTV_MTU)) { + if (mtu < IPV6_MMTU) { + /* xxx */ + rt->rt_rmx.rmx_locks |= RTV_MTU; + } else if (mtu < rt->rt_ifp->if_mtu && + rt->rt_rmx.rmx_mtu > mtu) { + rt->rt_rmx.rmx_mtu = mtu; + } + } + if (rt) + RTFREE(rt); +} + /* - * Process a Node Information Query + * Process a Node Information Query packet, (roughly) based on + * draft-ietf-ipngwg-icmp-name-lookups-05. + * + * Spec incompatibilities: + * - IPv6 Subject address handling + * - IPv4 Subject address handling support missing + * - Proxy reply (answer even if it's not for me) + * - "Supported Qtypes" support missing + * - joins NI group address at in6_ifattach() time only, does not cope + * with hostname changes by sethostname(3) */ -#define hostnamelen strlen(hostname) +#define hostnamelen strlen(hostname) #ifndef offsetof /* XXX */ #define offsetof(type, member) ((size_t)(&((type *)0)->member)) #endif - static struct mbuf * ni6_input(m, off) struct mbuf *m; int off; { - struct icmp6_nodeinfo *ni6 = - (struct icmp6_nodeinfo *)(mtod(m, caddr_t) + off), *nni6; + struct icmp6_nodeinfo *ni6, *nni6; struct mbuf *n = NULL; - u_int16_t qtype = ntohs(ni6->ni_qtype); + u_int16_t qtype; + int subjlen; int replylen = sizeof(struct ip6_hdr) + sizeof(struct icmp6_nodeinfo); struct ni_reply_fqdn *fqdn; int addrs; /* for NI_QTYPE_NODEADDR */ struct ifnet *ifp = NULL; /* for NI_QTYPE_NODEADDR */ + struct sockaddr_in6 sin6; + struct ip6_hdr *ip6; + int oldfqdn = 0; /* if 1, return pascal string (03 draft) */ + char *subj; - switch(qtype) { - case NI_QTYPE_NOOP: - break; /* no reply data */ - case NI_QTYPE_SUPTYPES: - goto bad; /* xxx: to be implemented */ - break; - case NI_QTYPE_FQDN: - replylen += offsetof(struct ni_reply_fqdn, ni_fqdn_name) + - hostnamelen; - break; - case NI_QTYPE_NODEADDR: - addrs = ni6_addrs(ni6, m, &ifp); - if ((replylen += addrs * sizeof(struct in6_addr)) > MCLBYTES) - replylen = MCLBYTES; /* XXX: we'll truncate later */ + ip6 = mtod(m, struct ip6_hdr *); +#ifndef PULLDOWN_TEST + ni6 = (struct icmp6_nodeinfo *)(mtod(m, caddr_t) + off); +#else + IP6_EXTHDR_GET(ni6, struct icmp6_nodeinfo *, m, off, sizeof(*ni6)); + if (ni6 == NULL) { + /* m is already reclaimed */ + return NULL; + } +#endif - break; - default: - /* - * XXX: We must return a reply with the ICMP6 code - * `unknown Qtype' in this case. However we regard the case - * as an FQDN query for backward compatibility. - * Older versions set a random value to this field, - * so it rarely varies in the defined qtypes. - * But the mechanism is not reliable... - * maybe we should obsolete older versions. - */ - qtype = NI_QTYPE_FQDN; - replylen += offsetof(struct ni_reply_fqdn, ni_fqdn_name) + - hostnamelen; - break; + /* + * Validate IPv6 destination address. + * + * We accept packets with the following IPv6 destination address: + * - Responder's unicast/anycast address, + * - link-local multicast address + * This is a violation to last paragraph in icmp-name-lookups-05 + * page 4, which restricts IPv6 destination address of a query to: + * - Responder's unicast/anycast address, + * - NI group address for a name belongs to the Responder, or + * - NI group address for a name for which the Responder is providing + * proxy service. + * (note: NI group address is a link-local multicast address) + * + * We allow any link-local multicast address, since "ping6 -w ff02::1" + * has been really useful for us debugging our network. Also this is + * still questionable if the restriction in spec buy us security at all, + * since RFC2463 permits echo packet to multicast destination. + * Even if we forbid NI query to ff02::1, we can effectively get the + * same result as "ping6 -w ff02::1" by the following steps: + * - run "ping6 ff02::1", then + * - run "ping6 -w" for all addresses replied. + */ + bzero(&sin6, sizeof(sin6)); + sin6.sin6_family = AF_INET6; + sin6.sin6_len = sizeof(struct sockaddr_in6); + bcopy(&ip6->ip6_dst, &sin6.sin6_addr, sizeof(sin6.sin6_addr)); + /* XXX scopeid */ + if (ifa_ifwithaddr((struct sockaddr *)&sin6)) + ; /*unicast/anycast, fine*/ + else if (IN6_IS_ADDR_MC_LINKLOCAL(&sin6.sin6_addr)) + ; /*violates spec slightly, see above*/ + else + goto bad; + + /* guess reply length */ + qtype = ntohs(ni6->ni_qtype); + switch (qtype) { + case NI_QTYPE_NOOP: + break; /* no reply data */ + case NI_QTYPE_SUPTYPES: + goto bad; /* xxx: to be implemented */ + break; + case NI_QTYPE_FQDN: + /* XXX will append a mbuf */ + replylen += offsetof(struct ni_reply_fqdn, ni_fqdn_namelen); + break; + case NI_QTYPE_NODEADDR: + addrs = ni6_addrs(ni6, m, &ifp); + if ((replylen += addrs * sizeof(struct in6_addr)) > MCLBYTES) + replylen = MCLBYTES; /* XXX: we'll truncate later */ + break; + default: + /* + * XXX: We must return a reply with the ICMP6 code + * `unknown Qtype' in this case. However we regard the case + * as an FQDN query for backward compatibility. + * Older versions set a random value to this field, + * so it rarely varies in the defined qtypes. + * But the mechanism is not reliable... + * maybe we should obsolete older versions. + */ + qtype = NI_QTYPE_FQDN; + /* XXX will append a mbuf */ + replylen += offsetof(struct ni_reply_fqdn, ni_fqdn_namelen); + oldfqdn++; + break; } + /* validate query Subject field. */ + subjlen = m->m_pkthdr.len - off - sizeof(struct icmp6_nodeinfo); + switch (qtype) { + case NI_QTYPE_NOOP: + case NI_QTYPE_SUPTYPES: + if (subjlen != 0) + goto bad; + break; + + case NI_QTYPE_FQDN: + case NI_QTYPE_NODEADDR: + switch (ni6->ni_code) { + case ICMP6_NI_SUBJ_IPV6: +#if ICMP6_NI_SUBJ_IPV6 != 0 + case 0: +#endif + /* + * backward compatibility - try to accept 03 draft + * format, where no Subject is present. + */ + if (subjlen == 0) { + oldfqdn++; + break; + } + + if (subjlen != sizeof(sin6.sin6_addr)) + goto bad; + + /* + * Validate Subject address. + * + * Not sure what exactly does "address belongs to the + * node" mean in the spec, is it just unicast, or what? + * + * At this moment we consider Subject address as + * "belong to the node" if the Subject address equals + * to the IPv6 destination address; validation for + * IPv6 destination address should have done enough + * check for us. + * + * We do not do proxy at this moment. + */ + /* m_pulldown instead of copy? */ + m_copydata(m, off + sizeof(struct icmp6_nodeinfo), + subjlen, (caddr_t)&sin6.sin6_addr); + /* XXX kame scope hack */ + if (IN6_IS_SCOPE_LINKLOCAL(&sin6.sin6_addr)) { +#ifdef FAKE_LOOPBACK_IF + if ((m->m_flags & M_PKTHDR) != 0 && + m->m_pkthdr.rcvif) { + sin6.sin6_addr.s6_addr16[1] = + htons(m->m_pkthdr.rcvif->if_index); + } +#else + if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) { + sin6.sin6_addr.s6_addr16[1] = + ip6->ip6_dst.s6_addr16[1]; + } +#endif + } + if (IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &sin6.sin6_addr)) + break; + /* + * XXX if we are to allow other cases, we should really + * be careful about scope here. + * basically, we should disallow queries toward IPv6 + * destination X with subject Y, if scope(X) > scope(Y). + * if we allow scope(X) > scope(Y), it will result in + * information leakage across scope boundary. + */ + goto bad; + + case ICMP6_NI_SUBJ_FQDN: + /* + * Validate Subject name with gethostname(3). + * + * The behavior may need some debate, since: + * - we are not sure if the node has FQDN as + * hostname (returned by gethostname(3)). + * - the code does wildcard match for truncated names. + * however, we are not sure if we want to perform + * wildcard match, if gethostname(3) side has + * truncated hostname. + */ + n = ni6_nametodns(hostname, hostnamelen, 0); + if (!n || n->m_next || n->m_len == 0) + goto bad; + IP6_EXTHDR_GET(subj, char *, m, + off + sizeof(struct icmp6_nodeinfo), subjlen); + if (subj == NULL) + goto bad; + if (!ni6_dnsmatch(subj, subjlen, mtod(n, const char *), + n->m_len)) { + goto bad; + } + m_freem(n); + n = NULL; + break; + + case ICMP6_NI_SUBJ_IPV4: /* xxx: to be implemented? */ + default: + goto bad; + } + break; + + default: + /* should never be here due to "switch (qtype)" above */ + goto bad; + } + /* allocate a mbuf to reply. */ MGETHDR(n, M_DONTWAIT, m->m_type); - if (n == NULL) + if (n == NULL) { + m_freem(m); return(NULL); + } M_COPY_PKTHDR(n, m); /* just for recvif */ if (replylen > MHLEN) { - if (replylen > MCLBYTES) + if (replylen > MCLBYTES) { /* * XXX: should we try to allocate more? But MCLBYTES is * probably much larger than IPV6_MMTU... */ goto bad; + } MCLGET(n, M_DONTWAIT); if ((n->m_flags & M_EXT) == 0) { goto bad; } } n->m_pkthdr.len = n->m_len = replylen; /* copy mbuf header and IPv6 + Node Information base headers */ bcopy(mtod(m, caddr_t), mtod(n, caddr_t), sizeof(struct ip6_hdr)); nni6 = (struct icmp6_nodeinfo *)(mtod(n, struct ip6_hdr *) + 1); - bcopy(mtod(m, caddr_t) + off, (caddr_t)nni6, sizeof(struct icmp6_nodeinfo)); + bcopy((caddr_t)ni6, (caddr_t)nni6, sizeof(struct icmp6_nodeinfo)); /* qtype dependent procedure */ switch (qtype) { - case NI_QTYPE_NOOP: - nni6->ni_flags = 0; - break; - case NI_QTYPE_SUPTYPES: - goto bad; /* xxx: to be implemented */ - break; - case NI_QTYPE_FQDN: - if (hostnamelen > 255) { /* XXX: rare case, but may happen */ - printf("ni6_input: " - "hostname length(%d) is too large for reply\n", - hostnamelen); - goto bad; - } - fqdn = (struct ni_reply_fqdn *)(mtod(n, caddr_t) + - sizeof(struct ip6_hdr) + - sizeof(struct icmp6_nodeinfo)); - nni6->ni_flags = 0; /* XXX: meaningless TTL */ - fqdn->ni_fqdn_ttl = 0; /* ditto. */ - fqdn->ni_fqdn_namelen = hostnamelen; - bcopy(hostname, &fqdn->ni_fqdn_name[0], hostnamelen); - break; - case NI_QTYPE_NODEADDR: - { - int lenlim, copied; + case NI_QTYPE_NOOP: + nni6->ni_flags = 0; + break; + case NI_QTYPE_SUPTYPES: + goto bad; /* xxx: to be implemented */ + break; + case NI_QTYPE_FQDN: + fqdn = (struct ni_reply_fqdn *)(mtod(n, caddr_t) + + sizeof(struct ip6_hdr) + + sizeof(struct icmp6_nodeinfo)); + nni6->ni_flags = 0; /* XXX: meaningless TTL */ + fqdn->ni_fqdn_ttl = 0; /* ditto. */ + /* + * XXX do we really have FQDN in variable "hostname"? + */ + n->m_next = ni6_nametodns(hostname, hostnamelen, oldfqdn); + if (n->m_next == NULL) + goto bad; + /* XXX we assume that n->m_next is not a chain */ + if (n->m_next->m_next != NULL) + goto bad; + n->m_pkthdr.len += n->m_next->m_len; + break; + case NI_QTYPE_NODEADDR: + { + int lenlim, copied; - if (n->m_flags & M_EXT) - lenlim = MCLBYTES - sizeof(struct ip6_hdr) - - sizeof(struct icmp6_nodeinfo); - else - lenlim = MHLEN - sizeof(struct ip6_hdr) - - sizeof(struct icmp6_nodeinfo); - copied = ni6_store_addrs(ni6, nni6, ifp, lenlim); - /* XXX: reset mbuf length */ - n->m_pkthdr.len = n->m_len = sizeof(struct ip6_hdr) + - sizeof(struct icmp6_nodeinfo) + copied; - break; - } - default: - break; /* XXX impossible! */ + if (n->m_flags & M_EXT) + lenlim = MCLBYTES - sizeof(struct ip6_hdr) - + sizeof(struct icmp6_nodeinfo); + else + lenlim = MHLEN - sizeof(struct ip6_hdr) - + sizeof(struct icmp6_nodeinfo); + copied = ni6_store_addrs(ni6, nni6, ifp, lenlim); + /* XXX: reset mbuf length */ + n->m_pkthdr.len = n->m_len = sizeof(struct ip6_hdr) + + sizeof(struct icmp6_nodeinfo) + copied; + break; } + default: + break; /* XXX impossible! */ + } nni6->ni_type = ICMP6_NI_REPLY; nni6->ni_code = ICMP6_NI_SUCESS; + m_freem(m); return(n); bad: + m_freem(m); if (n) m_freem(n); return(NULL); } #undef hostnamelen /* + * make a mbuf with DNS-encoded string. no compression support. + * + * XXX names with less than 2 dots (like "foo" or "foo.section") will be + * treated as truncated name (two \0 at the end). this is a wild guess. + */ +static struct mbuf * +ni6_nametodns(name, namelen, old) + const char *name; + int namelen; + int old; /* return pascal string if non-zero */ +{ + struct mbuf *m; + char *cp, *ep; + const char *p, *q; + int i, len, nterm; + + if (old) + len = namelen + 1; + else + len = MCLBYTES; + + /* because MAXHOSTNAMELEN is usually 256, we use cluster mbuf */ + MGET(m, M_DONTWAIT, MT_DATA); + if (m && len > MLEN) { + MCLGET(m, M_DONTWAIT); + if ((m->m_flags & M_EXT) == 0) + goto fail; + } + if (!m) + goto fail; + m->m_next = NULL; + + if (old) { + m->m_len = len; + *mtod(m, char *) = namelen; + bcopy(name, mtod(m, char *) + 1, namelen); + return m; + } else { + m->m_len = 0; + cp = mtod(m, char *); + ep = mtod(m, char *) + M_TRAILINGSPACE(m); + + /* if not certain about my name, return empty buffer */ + if (namelen == 0) + return m; + + /* + * guess if it looks like shortened hostname, or FQDN. + * shortened hostname needs two trailing "\0". + */ + i = 0; + for (p = name; p < name + namelen; p++) { + if (*p && *p == '.') + i++; + } + if (i < 2) + nterm = 2; + else + nterm = 1; + + p = name; + while (cp < ep && p < name + namelen) { + i = 0; + for (q = p; q < name + namelen && *q && *q != '.'; q++) + i++; + /* result does not fit into mbuf */ + if (cp + i + 1 >= ep) + goto fail; + /* DNS label length restriction, RFC1035 page 8 */ + if (i >= 64) + goto fail; + *cp++ = i; + bcopy(p, cp, i); + cp += i; + p = q; + if (p < name + namelen && *p == '.') + p++; + } + /* termination */ + if (cp + nterm >= ep) + goto fail; + while (nterm-- > 0) + *cp++ = '\0'; + m->m_len = cp - mtod(m, char *); + return m; + } + + panic("should not reach here"); + /*NOTREACHED*/ + + fail: + if (m) + m_freem(m); + return NULL; +} + +/* + * check if two DNS-encoded string matches. takes care of truncated + * form (with \0\0 at the end). no compression support. + */ +static int +ni6_dnsmatch(a, alen, b, blen) + const char *a; + int alen; + const char *b; + int blen; +{ + const char *a0, *b0; + int l; + + /* simplest case - need validation? */ + if (alen == blen && bcmp(a, b, alen) == 0) + return 1; + + a0 = a; + b0 = b; + + /* termination is mandatory */ + if (alen < 2 || blen < 2) + return 0; + if (a0[alen - 1] != '\0' || b0[blen - 1] != '\0') + return 0; + alen--; + blen--; + + while (a - a0 < alen && b - b0 < blen) { + if (a - a0 + 1 > alen || b - b0 + 1 > blen) + return 0; + + if ((signed char)a[0] < 0 || (signed char)b[0] < 0) + return 0; + /* we don't support compression yet */ + if (a[0] >= 64 || b[0] >= 64) + return 0; + + /* truncated case */ + if (a[0] == 0 && a - a0 == alen - 1) + return 1; + if (b[0] == 0 && b - b0 == blen - 1) + return 1; + if (a[0] == 0 || b[0] == 0) + return 0; + + if (a[0] != b[0]) + return 0; + l = a[0]; + if (a - a0 + 1 + l > alen || b - b0 + 1 + l > blen) + return 0; + if (bcmp(a + 1, b + 1, l) != 0) + return 0; + + a += 1 + l; + b += 1 + l; + } + + if (a - a0 == alen && b - b0 == blen) + return 1; + else + return 0; +} + +/* * calculate the number of addresses to be returned in the node info reply. */ static int ni6_addrs(ni6, m, ifpp) struct icmp6_nodeinfo *ni6; struct mbuf *m; struct ifnet **ifpp; { register struct ifnet *ifp; register struct in6_ifaddr *ifa6; register struct ifaddr *ifa; struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); int addrs = 0, addrsofif, iffound = 0; for (ifp = TAILQ_FIRST(&ifnet); ifp; ifp = TAILQ_NEXT(ifp, if_list)) { addrsofif = 0; - TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) + for (ifa = ifp->if_addrlist.tqh_first; ifa; + ifa = ifa->ifa_list.tqe_next) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; ifa6 = (struct in6_ifaddr *)ifa; if (!(ni6->ni_flags & NI_NODEADDR_FLAG_ALL) && IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &ifa6->ia_addr.sin6_addr)) iffound = 1; - if (ifa6->ia6_flags & IN6_IFF_ANYCAST) { - if ((ni6->ni_flags & NI_NODEADDR_FLAG_ANYCAST) - != 0) - addrsofif++; + /* + * IPv4-mapped addresses can only be returned by a + * Node Information proxy, since they represent + * addresses of IPv4-only nodes, which perforce do + * not implement this protocol. + * [icmp-name-lookups-05] + * So we don't support NI_NODEADDR_FLAG_COMPAT in + * this function at this moment. + */ + + if (ifa6->ia6_flags & IN6_IFF_ANYCAST) continue; /* we need only unicast addresses */ - } if ((ni6->ni_flags & (NI_NODEADDR_FLAG_LINKLOCAL | NI_NODEADDR_FLAG_SITELOCAL | NI_NODEADDR_FLAG_GLOBAL)) == 0) continue; /* What do we have to do about ::1? */ switch(in6_addrscope(&ifa6->ia_addr.sin6_addr)) { case IPV6_ADDR_SCOPE_LINKLOCAL: if (ni6->ni_flags & NI_NODEADDR_FLAG_LINKLOCAL) addrsofif++; break; case IPV6_ADDR_SCOPE_SITELOCAL: if (ni6->ni_flags & NI_NODEADDR_FLAG_SITELOCAL) addrsofif++; break; case IPV6_ADDR_SCOPE_GLOBAL: if (ni6->ni_flags & NI_NODEADDR_FLAG_GLOBAL) addrsofif++; break; default: continue; } } if (iffound) { *ifpp = ifp; return(addrsofif); } addrs += addrsofif; } return(addrs); } static int ni6_store_addrs(ni6, nni6, ifp0, resid) struct icmp6_nodeinfo *ni6, *nni6; struct ifnet *ifp0; int resid; { register struct ifnet *ifp = ifp0 ? ifp0 : TAILQ_FIRST(&ifnet); register struct in6_ifaddr *ifa6; register struct ifaddr *ifa; int docopy, copied = 0; u_char *cp = (u_char *)(nni6 + 1); if (ifp0 == NULL && !(ni6->ni_flags & NI_NODEADDR_FLAG_ALL)) return(0); /* needless to copy */ for (; ifp; ifp = TAILQ_NEXT(ifp, if_list)) { - TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) + for (ifa = ifp->if_addrlist.tqh_first; ifa; + ifa = ifa->ifa_list.tqe_next) { docopy = 0; if (ifa->ifa_addr->sa_family != AF_INET6) continue; ifa6 = (struct in6_ifaddr *)ifa; if (ifa6->ia6_flags & IN6_IFF_ANYCAST) { /* just experimental. not in the spec. */ if (ni6->ni_flags & NI_NODEADDR_FLAG_ANYCAST) docopy = 1; else continue; - } else { /* unicast address */ + } + else { /* unicast address */ if (ni6->ni_flags & NI_NODEADDR_FLAG_ANYCAST) continue; + else + docopy = 1; } /* What do we have to do about ::1? */ switch(in6_addrscope(&ifa6->ia_addr.sin6_addr)) { case IPV6_ADDR_SCOPE_LINKLOCAL: if (ni6->ni_flags & NI_NODEADDR_FLAG_LINKLOCAL) docopy = 1; break; case IPV6_ADDR_SCOPE_SITELOCAL: if (ni6->ni_flags & NI_NODEADDR_FLAG_SITELOCAL) docopy = 1; break; case IPV6_ADDR_SCOPE_GLOBAL: if (ni6->ni_flags & NI_NODEADDR_FLAG_GLOBAL) docopy = 1; break; default: continue; } if (docopy) { if (resid < sizeof(struct in6_addr)) { /* * We give up much more copy. * Set the truncate flag and return. */ nni6->ni_flags |= NI_NODEADDR_FLAG_TRUNCATE; return(copied); } bcopy(&ifa6->ia_addr.sin6_addr, cp, sizeof(struct in6_addr)); /* XXX: KAME link-local hack; remove ifindex */ if (IN6_IS_ADDR_LINKLOCAL(&ifa6->ia_addr.sin6_addr)) ((struct in6_addr *)cp)->s6_addr16[1] = 0; cp += sizeof(struct in6_addr); resid -= sizeof(struct in6_addr); copied += sizeof(struct in6_addr); } } if (ifp0) /* we need search only on the specified IF */ break; } return(copied); } /* * XXX almost dup'ed code with rip6_input. */ static int icmp6_rip6_input(mp, off) struct mbuf **mp; int off; { struct mbuf *m = *mp; register struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); register struct in6pcb *in6p; struct in6pcb *last = NULL; struct sockaddr_in6 rip6src; struct icmp6_hdr *icmp6; struct mbuf *opts = NULL; +#ifndef PULLDOWN_TEST /* this is assumed to be safe. */ icmp6 = (struct icmp6_hdr *)((caddr_t)ip6 + off); +#else + IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off, sizeof(*icmp6)); + if (icmp6 == NULL) { + /* m is already reclaimed */ + return IPPROTO_DONE; + } +#endif bzero(&rip6src, sizeof(rip6src)); rip6src.sin6_len = sizeof(struct sockaddr_in6); rip6src.sin6_family = AF_INET6; - rip6src.sin6_addr = ip6->ip6_src; - if (IN6_IS_SCOPE_LINKLOCAL(&rip6src.sin6_addr)) - rip6src.sin6_addr.s6_addr16[1] = 0; - if (m->m_pkthdr.rcvif) { - if (IN6_IS_SCOPE_LINKLOCAL(&rip6src.sin6_addr)) - rip6src.sin6_scope_id = m->m_pkthdr.rcvif->if_index; - else - rip6src.sin6_scope_id = 0; - } else - rip6src.sin6_scope_id = 0; + /* KAME hack: recover scopeid */ + (void)in6_recoverscope(&rip6src, &ip6->ip6_src, m->m_pkthdr.rcvif); LIST_FOREACH(in6p, &ripcb, inp_list) { - if ((in6p->inp_vflag & INP_IPV6) == 0) + if ((in6p->inp_vflag & INP_IPV6) == NULL) continue; if (in6p->in6p_ip6_nxt != IPPROTO_ICMPV6) continue; if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr) && !IN6_ARE_ADDR_EQUAL(&in6p->in6p_laddr, &ip6->ip6_dst)) continue; if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr) && !IN6_ARE_ADDR_EQUAL(&in6p->in6p_faddr, &ip6->ip6_src)) continue; if (in6p->in6p_icmp6filt && ICMP6_FILTER_WILLBLOCK(icmp6->icmp6_type, in6p->in6p_icmp6filt)) continue; if (last) { struct mbuf *n; if ((n = m_copy(m, 0, (int)M_COPYALL)) != NULL) { if (last->in6p_flags & IN6P_CONTROLOPTS) ip6_savecontrol(last, &opts, ip6, n); /* strip intermediate headers */ m_adj(n, off); if (sbappendaddr(&last->in6p_socket->so_rcv, (struct sockaddr *)&rip6src, n, opts) == 0) { /* should notify about lost packet */ m_freem(n); if (opts) m_freem(opts); } else sorwakeup(last->in6p_socket); opts = NULL; } } last = in6p; } if (last) { if (last->in6p_flags & IN6P_CONTROLOPTS) ip6_savecontrol(last, &opts, ip6, m); /* strip intermediate headers */ m_adj(m, off); if (sbappendaddr(&last->in6p_socket->so_rcv, (struct sockaddr *)&rip6src, m, opts) == 0) { m_freem(m); if (opts) m_freem(opts); } else sorwakeup(last->in6p_socket); } else { m_freem(m); ip6stat.ip6s_delivered--; } return IPPROTO_DONE; } /* * Reflect the ip6 packet back to the source. - * The caller MUST check if the destination is multicast or not. - * This function is usually called with a unicast destination which - * can be safely the source of the reply packet. But some exceptions - * exist(e.g. ECHOREPLY, PATCKET_TOOBIG, "10" in OPTION type). - * ``off'' points to the icmp6 header, counted from the top of the mbuf. + * OFF points to the icmp6 header, counted from the top of the mbuf. */ void icmp6_reflect(m, off) struct mbuf *m; size_t off; { - struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); + struct ip6_hdr *ip6; struct icmp6_hdr *icmp6; struct in6_ifaddr *ia; struct in6_addr t, *src = 0; - int plen = m->m_pkthdr.len - sizeof(struct ip6_hdr); + int plen; int type, code; struct ifnet *outif = NULL; #ifdef COMPAT_RFC1885 int mtu = IPV6_MMTU; struct sockaddr_in6 *sin6 = &icmp6_reflect_rt.ro_dst; #endif + /* too short to reflect */ + if (off < sizeof(struct ip6_hdr)) { + printf("sanity fail: off=%lx, sizeof(ip6)=%lx in %s:%d\n", + (u_long)off, (u_long)sizeof(struct ip6_hdr), + __FILE__, __LINE__); + goto bad; + } + /* * If there are extra headers between IPv6 and ICMPv6, strip * off that header first. */ - if (off != sizeof(struct ip6_hdr)) { - size_t siz; +#ifdef DIAGNOSTIC + if (sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr) > MHLEN) + panic("assumption failed in icmp6_reflect"); +#endif + if (off > sizeof(struct ip6_hdr)) { + size_t l; + struct ip6_hdr nip6; - /* sanity checks */ - if (off < sizeof(struct ip6_hdr)) { - printf("sanity fail: off=%x, sizeof(ip6)=%x in %s:%d\n", - (unsigned int)off, - (unsigned int)sizeof(struct ip6_hdr), - __FILE__, __LINE__); - goto bad; + l = off - sizeof(struct ip6_hdr); + m_copydata(m, 0, sizeof(nip6), (caddr_t)&nip6); + m_adj(m, l); + l = sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr); + if (m->m_len < l) { + if ((m = m_pullup(m, l)) == NULL) + return; } - siz = off - sizeof(struct ip6_hdr); - if (plen < siz) { - printf("sanity fail: siz=%x, payloadlen=%x in %s:%d\n", - (unsigned int)siz, plen, __FILE__, __LINE__); - goto bad; + bcopy((caddr_t)&nip6, mtod(m, caddr_t), sizeof(nip6)); + } else /* off == sizeof(struct ip6_hdr) */ { + size_t l; + l = sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr); + if (m->m_len < l) { + if ((m = m_pullup(m, l)) == NULL) + return; } - IP6_EXTHDR_CHECK(m, 0, off, /*nothing*/); - IP6_EXTHDR_CHECK(m, off, sizeof(struct icmp6_hdr), /*nothing*/); - - ovbcopy((caddr_t)ip6, - (caddr_t)(mtod(m, u_char *) + siz), - sizeof(struct ip6_hdr)); - m->m_data += siz; - m->m_len -= siz; - m->m_pkthdr.len -= siz; - ip6 = mtod(m, struct ip6_hdr *); - ip6->ip6_nxt = IPPROTO_ICMPV6; - plen -= siz; } - + plen = m->m_pkthdr.len - sizeof(struct ip6_hdr); + ip6 = mtod(m, struct ip6_hdr *); + ip6->ip6_nxt = IPPROTO_ICMPV6; icmp6 = (struct icmp6_hdr *)(ip6 + 1); type = icmp6->icmp6_type; /* keep type for statistics */ code = icmp6->icmp6_code; /* ditto. */ t = ip6->ip6_dst; /* * ip6_input() drops a packet if its src is multicast. * So, the src is never multicast. */ ip6->ip6_dst = ip6->ip6_src; /* XXX hack for link-local addresses */ if (IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_dst)) ip6->ip6_dst.s6_addr16[1] = htons(m->m_pkthdr.rcvif->if_index); if (IN6_IS_ADDR_LINKLOCAL(&t)) t.s6_addr16[1] = htons(m->m_pkthdr.rcvif->if_index); #ifdef COMPAT_RFC1885 /* * xxx guess MTU * RFC 1885 requires that echo reply should be truncated if it * does not fit in with (return) path MTU, but the description was * removed in the new spec. */ if (icmp6_reflect_rt.ro_rt == 0 || ! (IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, &ip6->ip6_dst))) { if (icmp6_reflect_rt.ro_rt) { RTFREE(icmp6_reflect_rt.ro_rt); icmp6_reflect_rt.ro_rt = 0; } bzero(sin6, sizeof(*sin6)); sin6->sin6_family = PF_INET6; sin6->sin6_len = sizeof(struct sockaddr_in6); sin6->sin6_addr = ip6->ip6_dst; rtalloc_ign((struct route *)&icmp6_reflect_rt.ro_rt, RTF_PRCLONING); } if (icmp6_reflect_rt.ro_rt == 0) goto bad; if ((icmp6_reflect_rt.ro_rt->rt_flags & RTF_HOST) && mtu < icmp6_reflect_rt.ro_rt->rt_ifp->if_mtu) mtu = icmp6_reflect_rt.ro_rt->rt_rmx.rmx_mtu; if (mtu < m->m_pkthdr.len) { plen -= (m->m_pkthdr.len - mtu); m_adj(m, mtu - m->m_pkthdr.len); } #endif /* * If the incoming packet was addressed directly to us(i.e. unicast), * use dst as the src for the reply. + * The IN6_IFF_NOTREADY case would be VERY rare, but is possible + * (for example) when we encounter an error while forwarding procedure + * destined to a duplicated address of ours. */ for (ia = in6_ifaddr; ia; ia = ia->ia_next) if (IN6_ARE_ADDR_EQUAL(&t, &ia->ia_addr.sin6_addr) && - (ia->ia6_flags & IN6_IFF_ANYCAST) == 0) { + (ia->ia6_flags & (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY)) == 0) { src = &t; break; } if (ia == NULL && IN6_IS_ADDR_LINKLOCAL(&t) && (m->m_flags & M_LOOP)) { /* * This is the case if the dst is our link-local address * and the sender is also ourseleves. */ src = &t; } if (src == 0) /* - * We have not multicast routing yet. So this case matches - * to our multicast, our anycast or not to our unicast. - * Select a source address which has the same scope. + * This case matches to multicasts, our anycast, or unicasts + * that we do not own. Select a source address which has the + * same scope. + * XXX: for (non link-local) multicast addresses, this might + * not be a good choice. */ if ((ia = in6_ifawithscope(m->m_pkthdr.rcvif, &t)) != 0) src = &IA6_SIN6(ia)->sin6_addr; if (src == 0) goto bad; ip6->ip6_src = *src; ip6->ip6_flow = 0; - ip6->ip6_vfc = IPV6_VERSION; + ip6->ip6_vfc &= ~IPV6_VERSION_MASK; + ip6->ip6_vfc |= IPV6_VERSION; ip6->ip6_nxt = IPPROTO_ICMPV6; if (m->m_pkthdr.rcvif) { /* XXX: This may not be the outgoing interface */ ip6->ip6_hlim = nd_ifinfo[m->m_pkthdr.rcvif->if_index].chlim; } icmp6->icmp6_cksum = 0; icmp6->icmp6_cksum = in6_cksum(m, IPPROTO_ICMPV6, sizeof(struct ip6_hdr), plen); /* * xxx option handling */ m->m_flags &= ~(M_BCAST|M_MCAST); +#ifdef IPSEC + /* Don't lookup socket */ + ipsec_setsocket(m, NULL); +#endif /*IPSEC*/ #ifdef COMPAT_RFC1885 ip6_output(m, NULL, &icmp6_reflect_rt, 0, NULL, &outif); #else ip6_output(m, NULL, NULL, 0, NULL, &outif); #endif if (outif) icmp6_ifoutstat_inc(outif, type, code); return; bad: m_freem(m); return; } void icmp6_fasttimo() { + mld6_fasttimeo(); + + /* reset ICMPv6 pps limit */ + icmp6errpps_count = 0; } static const char * icmp6_redirect_diag(src6, dst6, tgt6) struct in6_addr *src6; struct in6_addr *dst6; struct in6_addr *tgt6; { static char buf[1024]; snprintf(buf, sizeof(buf), "(src=%s dst=%s tgt=%s)", ip6_sprintf(src6), ip6_sprintf(dst6), ip6_sprintf(tgt6)); return buf; } void icmp6_redirect_input(m, off) register struct mbuf *m; int off; { struct ifnet *ifp = m->m_pkthdr.rcvif; struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); - struct nd_redirect *nd_rd = (struct nd_redirect *)((caddr_t)ip6 + off); + struct nd_redirect *nd_rd; int icmp6len = ntohs(ip6->ip6_plen); char *lladdr = NULL; int lladdrlen = 0; u_char *redirhdr = NULL; int redirhdrlen = 0; struct rtentry *rt = NULL; int is_router; int is_onlink; struct in6_addr src6 = ip6->ip6_src; - struct in6_addr redtgt6 = nd_rd->nd_rd_target; - struct in6_addr reddst6 = nd_rd->nd_rd_dst; + struct in6_addr redtgt6; + struct in6_addr reddst6; union nd_opts ndopts; if (!m || !ifp) return; /* XXX if we are router, we don't update route by icmp6 redirect */ if (ip6_forwarding) - return; + goto freeit; if (!icmp6_rediraccept) + goto freeit; + +#ifndef PULLDOWN_TEST + IP6_EXTHDR_CHECK(m, off, icmp6len,); + nd_rd = (struct nd_redirect *)((caddr_t)ip6 + off); +#else + IP6_EXTHDR_GET(nd_rd, struct nd_redirect *, m, off, icmp6len); + if (nd_rd == NULL) { + icmp6stat.icp6s_tooshort++; return; + } +#endif + redtgt6 = nd_rd->nd_rd_target; + reddst6 = nd_rd->nd_rd_dst; if (IN6_IS_ADDR_LINKLOCAL(&redtgt6)) redtgt6.s6_addr16[1] = htons(ifp->if_index); if (IN6_IS_ADDR_LINKLOCAL(&reddst6)) reddst6.s6_addr16[1] = htons(ifp->if_index); /* validation */ if (!IN6_IS_ADDR_LINKLOCAL(&src6)) { log(LOG_ERR, "ICMP6 redirect sent from %s rejected; " "must be from linklocal\n", ip6_sprintf(&src6)); - return; + goto freeit; } if (ip6->ip6_hlim != 255) { log(LOG_ERR, "ICMP6 redirect sent from %s rejected; " "hlim=%d (must be 255)\n", ip6_sprintf(&src6), ip6->ip6_hlim); - return; + goto freeit; } { /* ip6->ip6_src must be equal to gw for icmp6->icmp6_reddst */ struct sockaddr_in6 sin6; struct in6_addr *gw6; bzero(&sin6, sizeof(sin6)); sin6.sin6_family = AF_INET6; sin6.sin6_len = sizeof(struct sockaddr_in6); bcopy(&reddst6, &sin6.sin6_addr, sizeof(reddst6)); rt = rtalloc1((struct sockaddr *)&sin6, 0, 0UL); if (rt) { gw6 = &(((struct sockaddr_in6 *)rt->rt_gateway)->sin6_addr); if (bcmp(&src6, gw6, sizeof(struct in6_addr)) != 0) { log(LOG_ERR, "ICMP6 redirect rejected; " "not equal to gw-for-src=%s (must be same): " "%s\n", ip6_sprintf(gw6), icmp6_redirect_diag(&src6, &reddst6, &redtgt6)); RTFREE(rt); - return; + goto freeit; } } else { log(LOG_ERR, "ICMP6 redirect rejected; " "no route found for redirect dst: %s\n", icmp6_redirect_diag(&src6, &reddst6, &redtgt6)); - return; + goto freeit; } RTFREE(rt); rt = NULL; } if (IN6_IS_ADDR_MULTICAST(&reddst6)) { log(LOG_ERR, "ICMP6 redirect rejected; " "redirect dst must be unicast: %s\n", icmp6_redirect_diag(&src6, &reddst6, &redtgt6)); - return; + goto freeit; } is_router = is_onlink = 0; if (IN6_IS_ADDR_LINKLOCAL(&redtgt6)) is_router = 1; /* router case */ if (bcmp(&redtgt6, &reddst6, sizeof(redtgt6)) == 0) is_onlink = 1; /* on-link destination case */ if (!is_router && !is_onlink) { log(LOG_ERR, "ICMP6 redirect rejected; " "neither router case nor onlink case: %s\n", icmp6_redirect_diag(&src6, &reddst6, &redtgt6)); - return; + goto freeit; } /* validation passed */ icmp6len -= sizeof(*nd_rd); nd6_option_init(nd_rd + 1, icmp6len, &ndopts); if (nd6_options(&ndopts) < 0) { log(LOG_INFO, "icmp6_redirect_input: " "invalid ND option, rejected: %s\n", icmp6_redirect_diag(&src6, &reddst6, &redtgt6)); - return; + goto freeit; } if (ndopts.nd_opts_tgt_lladdr) { lladdr = (char *)(ndopts.nd_opts_tgt_lladdr + 1); lladdrlen = ndopts.nd_opts_tgt_lladdr->nd_opt_len << 3; } if (ndopts.nd_opts_rh) { redirhdrlen = ndopts.nd_opts_rh->nd_opt_rh_len; redirhdr = (u_char *)(ndopts.nd_opts_rh + 1); /* xxx */ } if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { log(LOG_INFO, "icmp6_redirect_input: lladdrlen mismatch for %s " "(if %d, icmp6 packet %d): %s\n", ip6_sprintf(&redtgt6), ifp->if_addrlen, lladdrlen - 2, icmp6_redirect_diag(&src6, &reddst6, &redtgt6)); } /* RFC 2461 8.3 */ nd6_cache_lladdr(ifp, &redtgt6, lladdr, lladdrlen, ND_REDIRECT, is_onlink ? ND_REDIRECT_ONLINK : ND_REDIRECT_ROUTER); if (!is_onlink) { /* better router case. perform rtredirect. */ /* perform rtredirect */ struct sockaddr_in6 sdst; struct sockaddr_in6 sgw; struct sockaddr_in6 ssrc; bzero(&sdst, sizeof(sdst)); bzero(&sgw, sizeof(sgw)); bzero(&ssrc, sizeof(ssrc)); sdst.sin6_family = sgw.sin6_family = ssrc.sin6_family = AF_INET6; sdst.sin6_len = sgw.sin6_len = ssrc.sin6_len = sizeof(struct sockaddr_in6); bcopy(&redtgt6, &sgw.sin6_addr, sizeof(struct in6_addr)); bcopy(&reddst6, &sdst.sin6_addr, sizeof(struct in6_addr)); bcopy(&src6, &ssrc.sin6_addr, sizeof(struct in6_addr)); rtredirect((struct sockaddr *)&sdst, (struct sockaddr *)&sgw, (struct sockaddr *)NULL, RTF_GATEWAY | RTF_HOST, (struct sockaddr *)&ssrc, (struct rtentry **)NULL); } /* finally update cached route in each socket via pfctlinput */ { struct sockaddr_in6 sdst; bzero(&sdst, sizeof(sdst)); sdst.sin6_family = AF_INET6; sdst.sin6_len = sizeof(struct sockaddr_in6); bcopy(&reddst6, &sdst.sin6_addr, sizeof(struct in6_addr)); pfctlinput(PRC_REDIRECT_HOST, (struct sockaddr *)&sdst); #ifdef IPSEC key_sa_routechange((struct sockaddr *)&sdst); #endif } + + freeit: + m_freem(m); } void icmp6_redirect_output(m0, rt) struct mbuf *m0; struct rtentry *rt; { struct ifnet *ifp; /* my outgoing interface */ struct in6_addr *ifp_ll6; struct in6_addr *router_ll6; struct ip6_hdr *sip6; /* m0 as struct ip6_hdr */ struct mbuf *m = NULL; /* newly allocated one */ struct ip6_hdr *ip6; /* m as struct ip6_hdr */ struct nd_redirect *nd_rd; size_t maxlen; u_char *p; struct ifnet *outif = NULL; + struct sockaddr_in6 src_sa; + icmp6_errcount(&icmp6stat.icp6s_outerrhist, ND_REDIRECT, 0); + /* if we are not router, we don't send icmp6 redirect */ if (!ip6_forwarding || ip6_accept_rtadv) goto fail; /* sanity check */ if (!m0 || !rt || !(rt->rt_flags & RTF_UP) || !(ifp = rt->rt_ifp)) goto fail; /* * Address check: * the source address must identify a neighbor, and * the destination address must not be a multicast address * [RFC 2461, sec 8.2] */ sip6 = mtod(m0, struct ip6_hdr *); - if (nd6_is_addr_neighbor(&sip6->ip6_src, ifp) == 0) + bzero(&src_sa, sizeof(src_sa)); + src_sa.sin6_family = AF_INET6; + src_sa.sin6_len = sizeof(src_sa); + src_sa.sin6_addr = sip6->ip6_src; + /* we don't currently use sin6_scope_id, but eventually use it */ + src_sa.sin6_scope_id = in6_addr2scopeid(ifp, &sip6->ip6_src); + if (nd6_is_addr_neighbor(&src_sa, ifp) == 0) goto fail; if (IN6_IS_ADDR_MULTICAST(&sip6->ip6_dst)) goto fail; /* what should we do here? */ /* rate limit */ if (icmp6_ratelimit(&sip6->ip6_src, ND_REDIRECT, 0)) goto fail; /* * Since we are going to append up to 1280 bytes (= IPV6_MMTU), * we almost always ask for an mbuf cluster for simplicity. * (MHLEN < IPV6_MMTU is almost always true) */ +#if IPV6_MMTU >= MCLBYTES +# error assumption failed about IPV6_MMTU and MCLBYTES +#endif MGETHDR(m, M_DONTWAIT, MT_HEADER); + if (m && IPV6_MMTU >= MHLEN) + MCLGET(m, M_DONTWAIT); if (!m) goto fail; - if (MHLEN < IPV6_MMTU) - MCLGET(m, M_DONTWAIT); maxlen = (m->m_flags & M_EXT) ? MCLBYTES : MHLEN; maxlen = min(IPV6_MMTU, maxlen); /* just for safety */ - if (maxlen < sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr)) + if (maxlen < sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr) + + ((sizeof(struct nd_opt_hdr) + ifp->if_addrlen + 7) & ~7)) { goto fail; + } { /* get ip6 linklocal address for ifp(my outgoing interface). */ - struct in6_ifaddr *ia = in6ifa_ifpforlinklocal(ifp); - if (ia == NULL) + struct in6_ifaddr *ia; + if ((ia = in6ifa_ifpforlinklocal(ifp, + IN6_IFF_NOTREADY| + IN6_IFF_ANYCAST)) == NULL) goto fail; ifp_ll6 = &ia->ia_addr.sin6_addr; } /* get ip6 linklocal address for the router. */ if (rt->rt_gateway && (rt->rt_flags & RTF_GATEWAY)) { struct sockaddr_in6 *sin6; sin6 = (struct sockaddr_in6 *)rt->rt_gateway; router_ll6 = &sin6->sin6_addr; if (!IN6_IS_ADDR_LINKLOCAL(router_ll6)) router_ll6 = (struct in6_addr *)NULL; } else router_ll6 = (struct in6_addr *)NULL; /* ip6 */ ip6 = mtod(m, struct ip6_hdr *); ip6->ip6_flow = 0; - ip6->ip6_vfc = IPV6_VERSION; + ip6->ip6_vfc &= ~IPV6_VERSION_MASK; + ip6->ip6_vfc |= IPV6_VERSION; /* ip6->ip6_plen will be set later */ ip6->ip6_nxt = IPPROTO_ICMPV6; ip6->ip6_hlim = 255; /* ip6->ip6_src must be linklocal addr for my outgoing if. */ bcopy(ifp_ll6, &ip6->ip6_src, sizeof(struct in6_addr)); bcopy(&sip6->ip6_src, &ip6->ip6_dst, sizeof(struct in6_addr)); /* ND Redirect */ nd_rd = (struct nd_redirect *)(ip6 + 1); nd_rd->nd_rd_type = ND_REDIRECT; nd_rd->nd_rd_code = 0; nd_rd->nd_rd_reserved = 0; if (rt->rt_flags & RTF_GATEWAY) { /* * nd_rd->nd_rd_target must be a link-local address in * better router cases. */ if (!router_ll6) goto fail; bcopy(router_ll6, &nd_rd->nd_rd_target, sizeof(nd_rd->nd_rd_target)); bcopy(&sip6->ip6_dst, &nd_rd->nd_rd_dst, sizeof(nd_rd->nd_rd_dst)); } else { /* make sure redtgt == reddst */ bcopy(&sip6->ip6_dst, &nd_rd->nd_rd_target, sizeof(nd_rd->nd_rd_target)); bcopy(&sip6->ip6_dst, &nd_rd->nd_rd_dst, sizeof(nd_rd->nd_rd_dst)); } p = (u_char *)(nd_rd + 1); if (!router_ll6) goto nolladdropt; { /* target lladdr option */ struct rtentry *rt_router = NULL; int len; struct sockaddr_dl *sdl; struct nd_opt_hdr *nd_opt; char *lladdr; rt_router = nd6_lookup(router_ll6, 0, ifp); if (!rt_router) goto nolladdropt; - if (!(rt_router->rt_flags & RTF_GATEWAY) - && (rt_router->rt_flags & RTF_LLINFO) - && (rt_router->rt_gateway->sa_family == AF_LINK) - && (sdl = (struct sockaddr_dl *)rt_router->rt_gateway)) { + len = sizeof(*nd_opt) + ifp->if_addrlen; + len = (len + 7) & ~7; /*round by 8*/ + /* safety check */ + if (len + (p - (u_char *)ip6) > maxlen) + goto nolladdropt; + if (!(rt_router->rt_flags & RTF_GATEWAY) && + (rt_router->rt_flags & RTF_LLINFO) && + (rt_router->rt_gateway->sa_family == AF_LINK) && + (sdl = (struct sockaddr_dl *)rt_router->rt_gateway) && + sdl->sdl_alen) { nd_opt = (struct nd_opt_hdr *)p; nd_opt->nd_opt_type = ND_OPT_TARGET_LINKADDR; - len = 2 + ifp->if_addrlen; - len = (len + 7) & ~7; /*round by 8*/ nd_opt->nd_opt_len = len >> 3; - p += len; lladdr = (char *)(nd_opt + 1); bcopy(LLADDR(sdl), lladdr, ifp->if_addrlen); + p += len; } } nolladdropt:; m->m_pkthdr.len = m->m_len = p - (u_char *)ip6; /* just to be safe */ +#ifdef M_DECRYPTED /*not openbsd*/ if (m0->m_flags & M_DECRYPTED) goto noredhdropt; +#endif + if (p - (u_char *)ip6 > maxlen) + goto noredhdropt; { /* redirected header option */ int len; struct nd_opt_rd_hdr *nd_opt_rh; /* * compute the maximum size for icmp6 redirect header option. * XXX room for auth header? */ len = maxlen - (p - (u_char *)ip6); len &= ~7; /* This is just for simplicity. */ if (m0->m_pkthdr.len != m0->m_len) { if (m0->m_next) { m_freem(m0->m_next); m0->m_next = NULL; } m0->m_pkthdr.len = m0->m_len; } /* * Redirected header option spec (RFC2461 4.6.3) talks nothing * about padding/truncate rule for the original IP packet. * From the discussion on IPv6imp in Feb 1999, the consensus was: * - "attach as much as possible" is the goal * - pad if not aligned (original size can be guessed by original * ip6 header) * Following code adds the padding if it is simple enough, * and truncates if not. */ if (m0->m_next || m0->m_pkthdr.len != m0->m_len) panic("assumption failed in %s:%d\n", __FILE__, __LINE__); if (len - sizeof(*nd_opt_rh) < m0->m_pkthdr.len) { /* not enough room, truncate */ m0->m_pkthdr.len = m0->m_len = len - sizeof(*nd_opt_rh); } else { /* enough room, pad or truncate */ size_t extra; extra = m0->m_pkthdr.len % 8; if (extra) { /* pad if easy enough, truncate if not */ if (8 - extra <= M_TRAILINGSPACE(m0)) { /* pad */ m0->m_len += (8 - extra); m0->m_pkthdr.len += (8 - extra); } else { /* truncate */ m0->m_pkthdr.len -= extra; m0->m_len -= extra; } } len = m0->m_pkthdr.len + sizeof(*nd_opt_rh); m0->m_pkthdr.len = m0->m_len = len - sizeof(*nd_opt_rh); } nd_opt_rh = (struct nd_opt_rd_hdr *)p; bzero(nd_opt_rh, sizeof(*nd_opt_rh)); nd_opt_rh->nd_opt_rh_type = ND_OPT_REDIRECTED_HEADER; nd_opt_rh->nd_opt_rh_len = len >> 3; p += sizeof(*nd_opt_rh); m->m_pkthdr.len = m->m_len = p - (u_char *)ip6; /* connect m0 to m */ m->m_next = m0; m->m_pkthdr.len = m->m_len + m0->m_len; } noredhdropt:; if (IN6_IS_ADDR_LINKLOCAL(&sip6->ip6_src)) sip6->ip6_src.s6_addr16[1] = 0; if (IN6_IS_ADDR_LINKLOCAL(&sip6->ip6_dst)) sip6->ip6_dst.s6_addr16[1] = 0; +#if 0 + if (IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_src)) + ip6->ip6_src.s6_addr16[1] = 0; + if (IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_dst)) + ip6->ip6_dst.s6_addr16[1] = 0; +#endif if (IN6_IS_ADDR_LINKLOCAL(&nd_rd->nd_rd_target)) nd_rd->nd_rd_target.s6_addr16[1] = 0; if (IN6_IS_ADDR_LINKLOCAL(&nd_rd->nd_rd_dst)) nd_rd->nd_rd_dst.s6_addr16[1] = 0; ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(struct ip6_hdr)); nd_rd->nd_rd_cksum = 0; nd_rd->nd_rd_cksum = in6_cksum(m, IPPROTO_ICMPV6, sizeof(*ip6), ntohs(ip6->ip6_plen)); /* send the packet to outside... */ +#ifdef IPSEC + /* Don't lookup socket */ + ipsec_setsocket(m, NULL); +#endif /*IPSEC*/ ip6_output(m, NULL, NULL, 0, NULL, &outif); if (outif) { icmp6_ifstat_inc(outif, ifs6_out_msg); icmp6_ifstat_inc(outif, ifs6_out_redirect); } icmp6stat.icp6s_outhist[ND_REDIRECT]++; return; fail: if (m) m_freem(m); if (m0) m_freem(m0); } /* * ICMPv6 socket option processing. + * + * NOTE: for OSes that use NRL inpcb (bsdi4/openbsd), do not forget to modify + * sys/netinet6/raw_ipv6.c:rip6_ctloutput(). */ int icmp6_ctloutput(so, sopt) struct socket *so; struct sockopt *sopt; { int error = 0; int optlen; register struct inpcb *inp = sotoinpcb(so); int level, op, optname; if (sopt) { level = sopt->sopt_level; op = sopt->sopt_dir; optname = sopt->sopt_name; optlen = sopt->sopt_valsize; } else level = op = optname = optlen = 0; if (level != IPPROTO_ICMPV6) { return EINVAL; } switch(op) { case PRCO_SETOPT: switch (optname) { case ICMP6_FILTER: { struct icmp6_filter *p; if (optlen != sizeof(*p)) { error = EMSGSIZE; break; } if (inp->in6p_icmp6filt == NULL) { error = EINVAL; break; } error = sooptcopyin(sopt, inp->in6p_icmp6filt, optlen, optlen); break; } default: error = ENOPROTOOPT; break; } break; case PRCO_GETOPT: switch (optname) { case ICMP6_FILTER: { if (inp->in6p_icmp6filt == NULL) { error = EINVAL; break; } error = sooptcopyout(sopt, inp->in6p_icmp6filt, sizeof(struct icmp6_filter)); break; } default: error = ENOPROTOOPT; break; } break; } return(error); } +#ifndef HAVE_RATECHECK /* + * ratecheck() returns true if it is okay to send. We return + * true if it is not okay to send. + */ +static int +ratecheck(last, limit) + struct timeval *last; + struct timeval *limit; +{ + struct timeval tp; + struct timeval nextsend; + + microtime(&tp); + tp.tv_sec = time_second; + + /* rate limit */ + if (last->tv_sec != 0 || last->tv_usec != 0) { + nextsend.tv_sec = last->tv_sec + limit->tv_sec; + nextsend.tv_usec = last->tv_usec + limit->tv_usec; + nextsend.tv_sec += (nextsend.tv_usec / 1000000); + nextsend.tv_usec %= 1000000; + + if (nextsend.tv_sec == tp.tv_sec && nextsend.tv_usec <= tp.tv_usec) + ; + else if (nextsend.tv_sec <= tp.tv_sec) + ; + else { + /* The packet is subject to rate limit */ + return 0; + } + } + + *last = tp; + return 1; +} +#endif + +/* * Perform rate limit check. * Returns 0 if it is okay to send the icmp6 packet. * Returns 1 if the router SHOULD NOT send this icmp6 packet due to rate * limitation. * + * There are two limitations defined: + * - pps limit: ICMPv6 error packet cannot exceed defined packet-per-second. + * we measure it every 0.2 second, since fasttimo works every 0.2 second. + * - rate limit: ICMPv6 error packet cannot appear more than once per + * defined interval. + * In any case, if we perform rate limitation, we'll see jitter in the ICMPv6 + * error packets. + * * XXX per-destination/type check necessary? */ static int icmp6_ratelimit(dst, type, code) const struct in6_addr *dst; /* not used at this moment */ const int type; /* not used at this moment */ const int code; /* not used at this moment */ { - struct timeval tp; - long sec_diff, usec_diff; + int ret; - /* If we are not doing rate limitation, it is always okay to send */ - if (!icmp6errratelim) - return 0; + ret = 0; /*okay to send*/ - microtime(&tp); - tp.tv_sec = time_second; - if (tp.tv_sec < icmp6_nextsend.tv_sec - || (tp.tv_sec == icmp6_nextsend.tv_sec - && tp.tv_usec < icmp6_nextsend.tv_usec)) { + /* PPS limit */ + icmp6errpps_count++; + if (icmp6errppslim && icmp6errpps_count > icmp6errppslim / 5) { + /* The packet is subject to pps limit */ + ret++; + } + + if (!ratecheck(&icmp6errratelim_last, &icmp6errratelim)) { /* The packet is subject to rate limit */ - return 1; + ret++; } - sec_diff = icmp6errratelim / 1000000; - usec_diff = icmp6errratelim % 1000000; - icmp6_nextsend.tv_sec = tp.tv_sec + sec_diff; - if ((tp.tv_usec = tp.tv_usec + usec_diff) >= 1000000) { - icmp6_nextsend.tv_sec++; - icmp6_nextsend.tv_usec -= 1000000; - } - /* it is okay to send this */ - return 0; + return ret; } Index: head/sys/netinet6/icmp6.h =================================================================== --- head/sys/netinet6/icmp6.h (revision 62586) +++ head/sys/netinet6/icmp6.h (revision 62587) @@ -1,615 +1,4 @@ -/* - * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the project nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * $FreeBSD$ - */ +/* $FreeBSD$ */ +/* $KAME: icmp6.h,v 1.17 2000/06/11 17:23:40 jinmei Exp $ */ -/* - * Copyright (c) 1982, 1986, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)ip_icmp.h 8.1 (Berkeley) 6/10/93 - */ - -#ifndef _NETINET6_ICMPV6_H_ -#define _NETINET6_ICMPV6_H_ - -#if !defined(_KERNEL) && !defined(__KAME_NETINET_ICMP6_H_INCLUDED_) -#error "do not include netinet6/icmp6.h directly, include netinet/icmp6.h" -#endif - -#define ICMPV6_PLD_MAXLEN 1232 /* IPV6_MMTU - sizeof(struct ip6_hdr) - - sizeof(struct icmp6_hdr) */ - -struct icmp6_hdr { - u_int8_t icmp6_type; /* type field */ - u_int8_t icmp6_code; /* code field */ - u_int16_t icmp6_cksum; /* checksum field */ - union { - u_int32_t icmp6_un_data32[1]; /* type-specific field */ - u_int16_t icmp6_un_data16[2]; /* type-specific field */ - u_int8_t icmp6_un_data8[4]; /* type-specific field */ - } icmp6_dataun; -}; - -#define icmp6_data32 icmp6_dataun.icmp6_un_data32 -#define icmp6_data16 icmp6_dataun.icmp6_un_data16 -#define icmp6_data8 icmp6_dataun.icmp6_un_data8 -#define icmp6_pptr icmp6_data32[0] /* parameter prob */ -#define icmp6_mtu icmp6_data32[0] /* packet too big */ -#define icmp6_id icmp6_data16[0] /* echo request/reply */ -#define icmp6_seq icmp6_data16[1] /* echo request/reply */ -#define icmp6_maxdelay icmp6_data16[0] /* mcast group membership */ - -#define ICMP6_DST_UNREACH 1 /* dest unreachable, codes: */ -#define ICMP6_PACKET_TOO_BIG 2 /* packet too big */ -#define ICMP6_TIME_EXCEEDED 3 /* time exceeded, code: */ -#define ICMP6_PARAM_PROB 4 /* ip6 header bad */ - -#define ICMP6_ECHO_REQUEST 128 /* echo service */ -#define ICMP6_ECHO_REPLY 129 /* echo reply */ -#define ICMP6_MEMBERSHIP_QUERY 130 /* group membership query */ -#define MLD6_LISTENER_QUERY 130 /* multicast listener query */ -#define ICMP6_MEMBERSHIP_REPORT 131 /* group membership report */ -#define MLD6_LISTENER_REPORT 131 /* multicast listener report */ -#define ICMP6_MEMBERSHIP_REDUCTION 132 /* group membership termination */ -#define MLD6_LISTENER_DONE 132 /* multicast listener done */ - -#define ND_ROUTER_SOLICIT 133 /* router solicitation */ -#define ND_ROUTER_ADVERT 134 /* router advertisment */ -#define ND_NEIGHBOR_SOLICIT 135 /* neighbor solicitation */ -#define ND_NEIGHBOR_ADVERT 136 /* neighbor advertisment */ -#define ND_REDIRECT 137 /* redirect */ - -#define ICMP6_ROUTER_RENUMBERING 138 /* router renumbering */ - -#define ICMP6_WRUREQUEST 139 /* who are you request */ -#define ICMP6_WRUREPLY 140 /* who are you reply */ -#define ICMP6_FQDN_QUERY 139 /* FQDN query */ -#define ICMP6_FQDN_REPLY 140 /* FQDN reply */ -#define ICMP6_NI_QUERY 139 /* node information request */ -#define ICMP6_NI_REPLY 140 /* node information reply */ - -/* The definitions below are experimental. TBA */ -#define MLD6_MTRACE_RESP 141 /* mtrace response(to sender) */ -#define MLD6_MTRACE 142 /* mtrace messages */ - -#define ICMP6_MAXTYPE 142 - -#define ICMP6_DST_UNREACH_NOROUTE 0 /* no route to destination */ -#define ICMP6_DST_UNREACH_ADMIN 1 /* administratively prohibited */ -#define ICMP6_DST_UNREACH_NOTNEIGHBOR 2 /* not a neighbor(obsolete) */ -#define ICMP6_DST_UNREACH_BEYONDSCOPE 2 /* beyond scope of source address */ -#define ICMP6_DST_UNREACH_ADDR 3 /* address unreachable */ -#define ICMP6_DST_UNREACH_NOPORT 4 /* port unreachable */ - -#define ICMP6_TIME_EXCEED_TRANSIT 0 /* ttl==0 in transit */ -#define ICMP6_TIME_EXCEED_REASSEMBLY 1 /* ttl==0 in reass */ - -#define ICMP6_PARAMPROB_HEADER 0 /* erroneous header field */ -#define ICMP6_PARAMPROB_NEXTHEADER 1 /* unrecognized next header */ -#define ICMP6_PARAMPROB_OPTION 2 /* unrecognized option */ - -#define ICMP6_INFOMSG_MASK 0x80 /* all informational messages */ - -#define ICMP6_NI_SUCESS 0 /* node information successful reply */ -#define ICMP6_NI_REFUSED 1 /* node information request is refused */ -#define ICMP6_NI_UNKNOWN 2 /* unknown Qtype */ - -#define ICMP6_ROUTER_RENUMBERING_COMMAND 0 /* rr command */ -#define ICMP6_ROUTER_RENUMBERING_RESULT 1 /* rr result */ -#define ICMP6_ROUTER_RENUMBERING_SEQNUM_RESET 255 /* rr seq num reset */ - -/* Used in kernel only */ -#define ND_REDIRECT_ONLINK 0 /* redirect to an on-link node */ -#define ND_REDIRECT_ROUTER 1 /* redirect to a better router */ - -/* - * Multicast Listener Discovery - */ -struct mld6_hdr { - struct icmp6_hdr mld6_hdr; - struct in6_addr mld6_addr; /* multicast address */ -}; - -#define mld6_type mld6_hdr.icmp6_type -#define mld6_code mld6_hdr.icmp6_code -#define mld6_cksum mld6_hdr.icmp6_cksum -#define mld6_maxdelay mld6_hdr.icmp6_data16[0] -#define mld6_reserved mld6_hdr.icmp6_data16[1] - -/* - * Neighbor Discovery - */ - -struct nd_router_solicit { /* router solicitation */ - struct icmp6_hdr nd_rs_hdr; - /* could be followed by options */ -}; - -#define nd_rs_type nd_rs_hdr.icmp6_type -#define nd_rs_code nd_rs_hdr.icmp6_code -#define nd_rs_cksum nd_rs_hdr.icmp6_cksum -#define nd_rs_reserved nd_rs_hdr.icmp6_data32[0] - -struct nd_router_advert { /* router advertisement */ - struct icmp6_hdr nd_ra_hdr; - u_int32_t nd_ra_reachable; /* reachable time */ - u_int32_t nd_ra_retransmit; /* retransmit timer */ - /* could be followed by options */ -}; - -#define nd_ra_type nd_ra_hdr.icmp6_type -#define nd_ra_code nd_ra_hdr.icmp6_code -#define nd_ra_cksum nd_ra_hdr.icmp6_cksum -#define nd_ra_curhoplimit nd_ra_hdr.icmp6_data8[0] -#define nd_ra_flags_reserved nd_ra_hdr.icmp6_data8[1] -#define ND_RA_FLAG_MANAGED 0x80 -#define ND_RA_FLAG_OTHER 0x40 -#define nd_ra_router_lifetime nd_ra_hdr.icmp6_data16[1] - -struct nd_neighbor_solicit { /* neighbor solicitation */ - struct icmp6_hdr nd_ns_hdr; - struct in6_addr nd_ns_target; /*target address */ - /* could be followed by options */ -}; - -#define nd_ns_type nd_ns_hdr.icmp6_type -#define nd_ns_code nd_ns_hdr.icmp6_code -#define nd_ns_cksum nd_ns_hdr.icmp6_cksum -#define nd_ns_reserved nd_ns_hdr.icmp6_data32[0] - -struct nd_neighbor_advert { /* neighbor advertisement */ - struct icmp6_hdr nd_na_hdr; - struct in6_addr nd_na_target; /* target address */ - /* could be followed by options */ -}; - -#define nd_na_type nd_na_hdr.icmp6_type -#define nd_na_code nd_na_hdr.icmp6_code -#define nd_na_cksum nd_na_hdr.icmp6_cksum -#define nd_na_flags_reserved nd_na_hdr.icmp6_data32[0] -#if BYTE_ORDER == BIG_ENDIAN -#define ND_NA_FLAG_ROUTER 0x80000000 -#define ND_NA_FLAG_SOLICITED 0x40000000 -#define ND_NA_FLAG_OVERRIDE 0x20000000 -#elif BYTE_ORDER == LITTLE_ENDIAN -#define ND_NA_FLAG_ROUTER 0x80 -#define ND_NA_FLAG_SOLICITED 0x40 -#define ND_NA_FLAG_OVERRIDE 0x20 -#endif - -struct nd_redirect { /* redirect */ - struct icmp6_hdr nd_rd_hdr; - struct in6_addr nd_rd_target; /* target address */ - struct in6_addr nd_rd_dst; /* destination address */ - /* could be followed by options */ -}; - -#define nd_rd_type nd_rd_hdr.icmp6_type -#define nd_rd_code nd_rd_hdr.icmp6_code -#define nd_rd_cksum nd_rd_hdr.icmp6_cksum -#define nd_rd_reserved nd_rd_hdr.icmp6_data32[0] - -struct nd_opt_hdr { /* Neighbor discovery option header */ - u_int8_t nd_opt_type; - u_int8_t nd_opt_len; - /* followed by option specific data*/ -}; - -#define ND_OPT_SOURCE_LINKADDR 1 -#define ND_OPT_TARGET_LINKADDR 2 -#define ND_OPT_PREFIX_INFORMATION 3 -#define ND_OPT_REDIRECTED_HEADER 4 -#define ND_OPT_MTU 5 - -struct nd_opt_prefix_info { /* prefix information */ - u_int8_t nd_opt_pi_type; - u_int8_t nd_opt_pi_len; - u_int8_t nd_opt_pi_prefix_len; - u_int8_t nd_opt_pi_flags_reserved; - u_int32_t nd_opt_pi_valid_time; - u_int32_t nd_opt_pi_preferred_time; - u_int32_t nd_opt_pi_reserved2; - struct in6_addr nd_opt_pi_prefix; -}; - -#define ND_OPT_PI_FLAG_ONLINK 0x80 -#define ND_OPT_PI_FLAG_AUTO 0x40 - -struct nd_opt_rd_hdr { /* redirected header */ - u_int8_t nd_opt_rh_type; - u_int8_t nd_opt_rh_len; - u_int16_t nd_opt_rh_reserved1; - u_int32_t nd_opt_rh_reserved2; - /* followed by IP header and data */ -}; - -struct nd_opt_mtu { /* MTU option */ - u_int8_t nd_opt_mtu_type; - u_int8_t nd_opt_mtu_len; - u_int16_t nd_opt_mtu_reserved; - u_int32_t nd_opt_mtu_mtu; -}; - -/* - * icmp6 namelookup - */ - -struct icmp6_namelookup { - struct icmp6_hdr icmp6_nl_hdr; - u_int64_t icmp6_nl_nonce; - u_int32_t icmp6_nl_ttl; - /* could be followed by options */ -}; - -/* - * icmp6 node information - */ -struct icmp6_nodeinfo { - struct icmp6_hdr icmp6_ni_hdr; - u_int64_t icmp6_ni_nonce; - /* could be followed by reply data */ -}; - -#define ni_type icmp6_ni_hdr.icmp6_type -#define ni_code icmp6_ni_hdr.icmp6_code -#define ni_cksum icmp6_ni_hdr.icmp6_cksum -#define ni_qtype icmp6_ni_hdr.icmp6_data16[0] -#define ni_flags icmp6_ni_hdr.icmp6_data16[1] - - -#define NI_QTYPE_NOOP 0 /* NOOP */ -#define NI_QTYPE_SUPTYPES 1 /* Supported Qtypes */ -#define NI_QTYPE_FQDN 2 /* FQDN */ -#define NI_QTYPE_NODEADDR 3 /* Node Addresses. XXX: spec says 2, but it may be a typo... */ - -#if BYTE_ORDER == BIG_ENDIAN -#define NI_SUPTYPE_FLAG_COMPRESS 0x1 -#define NI_FQDN_FLAG_VALIDTTL 0x1 -#elif BYTE_ORDER == LITTLE_ENDIAN -#define NI_SUPTYPE_FLAG_COMPRESS 0x0100 -#define NI_FQDN_FLAG_VALIDTTL 0x0100 -#endif - -#if BYTE_ORDER == BIG_ENDIAN -#define NI_NODEADDR_FLAG_TRUNCATE 0x1 -#define NI_NODEADDR_FLAG_ALL 0x2 -#define NI_NODEADDR_FLAG_COMPAT 0x4 -#define NI_NODEADDR_FLAG_LINKLOCAL 0x8 -#define NI_NODEADDR_FLAG_SITELOCAL 0x10 -#define NI_NODEADDR_FLAG_GLOBAL 0x20 -#define NI_NODEADDR_FLAG_ANYCAST 0x40 /* just experimental. not in spec */ -#elif BYTE_ORDER == LITTLE_ENDIAN -#define NI_NODEADDR_FLAG_TRUNCATE 0x0100 -#define NI_NODEADDR_FLAG_ALL 0x0200 -#define NI_NODEADDR_FLAG_COMPAT 0x0400 -#define NI_NODEADDR_FLAG_LINKLOCAL 0x0800 -#define NI_NODEADDR_FLAG_SITELOCAL 0x1000 -#define NI_NODEADDR_FLAG_GLOBAL 0x2000 -#define NI_NODEADDR_FLAG_ANYCAST 0x4000 /* just experimental. not in spec */ -#endif - -struct ni_reply_fqdn { - u_int32_t ni_fqdn_ttl; /* TTL */ - u_int8_t ni_fqdn_namelen; /* length in octets of the FQDN */ - u_int8_t ni_fqdn_name[3]; /* XXX: alignment */ -}; - -/* - * Router Renumbering. as router-renum-08.txt - */ -struct icmp6_router_renum { /* router renumbering header */ - struct icmp6_hdr rr_hdr; - u_int8_t rr_segnum; - u_int8_t rr_flags; - u_int16_t rr_maxdelay; - u_int32_t rr_reserved; -}; -#define ICMP6_RR_FLAGS_SEGNUM 0x80 -#define ICMP6_RR_FLAGS_TEST 0x40 -#define ICMP6_RR_FLAGS_REQRESULT 0x20 -#define ICMP6_RR_FLAGS_FORCEAPPLY 0x10 -#define ICMP6_RR_FLAGS_SPECSITE 0x08 -#define ICMP6_RR_FLAGS_PREVDONE 0x04 - -#define rr_type rr_hdr.icmp6_type -#define rr_code rr_hdr.icmp6_code -#define rr_cksum rr_hdr.icmp6_cksum -#define rr_seqnum rr_hdr.icmp6_data32[0] - -struct rr_pco_match { /* match prefix part */ - u_int8_t rpm_code; - u_int8_t rpm_len; - u_int8_t rpm_ordinal; - u_int8_t rpm_matchlen; - u_int8_t rpm_minlen; - u_int8_t rpm_maxlen; - u_int16_t rpm_reserved; - struct in6_addr rpm_prefix; -}; - -#define RPM_PCO_ADD 1 -#define RPM_PCO_CHANGE 2 -#define RPM_PCO_SETGLOBAL 3 - -struct rr_pco_use { /* use prefix part */ - u_int8_t rpu_uselen; - u_int8_t rpu_keeplen; - u_int8_t rpu_ramask; - u_int8_t rpu_raflags; - u_int32_t rpu_vltime; - u_int32_t rpu_pltime; - u_int32_t rpu_flags; - struct in6_addr rpu_prefix; -}; -#define ICMP6_RR_PCOUSE_RAFLAGS_ONLINK 0x80 -#define ICMP6_RR_PCOUSE_RAFLAGS_AUTO 0x40 - -#if BYTE_ORDER == BIG_ENDIAN -#define ICMP6_RR_PCOUSE_FLAGS_DECRVLTIME 0x80000000 -#define ICMP6_RR_PCOUSE_FLAGS_DECRPLTIME 0x40000000 -#elif BYTE_ORDER == LITTLE_ENDIAN -#define ICMP6_RR_PCOUSE_FLAGS_DECRVLTIME 0x80 -#define ICMP6_RR_PCOUSE_FLAGS_DECRPLTIME 0x40 -#endif - -struct rr_result { /* router renumbering result message */ - u_int16_t rrr_flags; - u_int8_t rrr_ordinal; - u_int8_t rrr_matchedlen; - u_int32_t rrr_ifid; - struct in6_addr rrr_prefix; -}; -#if BYTE_ORDER == BIG_ENDIAN -#define ICMP6_RR_RESULT_FLAGS_OOB 0x0002 -#define ICMP6_RR_RESULT_FLAGS_FORBIDDEN 0x0001 -#elif BYTE_ORDER == LITTLE_ENDIAN -#define ICMP6_RR_RESULT_FLAGS_OOB 0x02 -#define ICMP6_RR_RESULT_FLAGS_FORBIDDEN 0x01 -#endif - -/* - * icmp6 filter structures. - */ - -struct icmp6_filter { - u_int32_t icmp6_filter[8]; -}; - -#ifdef _KERNEL -#define ICMP6_FILTER_SETPASSALL(filterp) \ - { \ - int i; u_char *p; \ - p = (u_char *)filterp; \ - for (i = 0; i < sizeof(struct icmp6_filter); i++) \ - p[i] = 0xff; \ - } -#define ICMP6_FILTER_SETBLOCKALL(filterp) \ - bzero(filterp, sizeof(struct icmp6_filter)) -#else /* _KERNEL */ -#define ICMP6_FILTER_SETPASSALL(filterp) \ - memset(filterp, 0xff, sizeof(struct icmp6_filter)) -#define ICMP6_FILTER_SETBLOCKALL(filterp) \ - memset(filterp, 0x00, sizeof(struct icmp6_filter)) -#endif /* _KERNEL */ - -#define ICMP6_FILTER_SETPASS(type, filterp) \ - (((filterp)->icmp6_filter[(type) >> 5]) |= (1 << ((type) & 31))) -#define ICMP6_FILTER_SETBLOCK(type, filterp) \ - (((filterp)->icmp6_filter[(type) >> 5]) &= ~(1 << ((type) & 31))) -#define ICMP6_FILTER_WILLPASS(type, filterp) \ - ((((filterp)->icmp6_filter[(type) >> 5]) & (1 << ((type) & 31))) != 0) -#define ICMP6_FILTER_WILLBLOCK(type, filterp) \ - ((((filterp)->icmp6_filter[(type) >> 5]) & (1 << ((type) & 31))) == 0) - -/* - * Variables related to this implementation - * of the internet control message protocol version 6. - */ -struct icmp6stat { -/* statistics related to icmp6 packets generated */ - u_long icp6s_error; /* # of calls to icmp6_error */ - u_long icp6s_canterror; /* no error 'cuz old was icmp */ - u_long icp6s_toofreq; /* no error 'cuz rate limitation */ - u_long icp6s_outhist[256]; -/* statistics related to input messages proccesed */ - u_long icp6s_badcode; /* icmp6_code out of range */ - u_long icp6s_tooshort; /* packet < sizeof(struct icmp6_hdr) */ - u_long icp6s_checksum; /* bad checksum */ - u_long icp6s_badlen; /* calculated bound mismatch */ - u_long icp6s_reflect; /* number of responses */ - u_long icp6s_inhist[256]; -}; - -/* - * Names for ICMP sysctl objects - */ -#define ICMPV6CTL_STATS 1 -#define ICMPV6CTL_REDIRACCEPT 2 /* accept/process redirects */ -#define ICMPV6CTL_REDIRTIMEOUT 3 /* redirect cache time */ -#define ICMPV6CTL_ERRRATELIMIT 5 /* ICMPv6 error rate limitation */ -#define ICMPV6CTL_ND6_PRUNE 6 -#define ICMPV6CTL_ND6_DELAY 8 -#define ICMPV6CTL_ND6_UMAXTRIES 9 -#define ICMPV6CTL_ND6_MMAXTRIES 10 -#define ICMPV6CTL_ND6_USELOOPBACK 11 -#define ICMPV6CTL_ND6_PROXYALL 12 -#define ICMPV6CTL_MAXID 13 - -#define ICMPV6CTL_NAMES { \ - { 0, 0 }, \ - { 0, 0 }, \ - { "rediraccept", CTLTYPE_INT }, \ - { "redirtimeout", CTLTYPE_INT }, \ - { 0, 0 }, \ - { "errratelimit", CTLTYPE_INT }, \ - { "nd6_prune", CTLTYPE_INT }, \ - { 0, 0 }, \ - { "nd6_delay", CTLTYPE_INT }, \ - { "nd6_umaxtries", CTLTYPE_INT }, \ - { "nd6_mmaxtries", CTLTYPE_INT }, \ - { "nd6_useloopback", CTLTYPE_INT }, \ - { "nd6_proxyall", CTLTYPE_INT }, \ -} - -#define ICMPV6CTL_VARS { \ - 0, \ - 0, \ - &icmp6_rediraccept, \ - &icmp6_redirtimeout, \ - 0, \ - 0, \ - &icmp6errratelim, \ - &nd6_prune, \ - 0, \ - &nd6_delay, \ - &nd6_umaxtries, \ - &nd6_mmaxtries, \ - &nd6_useloopback, \ - &nd6_proxyall, \ -} - -#define RTF_PROBEMTU RTF_PROTO1 - -#ifdef _KERNEL -#ifdef SYSCTL_DECL -SYSCTL_DECL(_net_inet6_icmp6); -#endif -# ifdef __STDC__ -struct rtentry; -struct rttimer; -struct in6_multi; -# endif -void icmp6_init __P((void)); -void icmp6_paramerror __P((struct mbuf *, int)); -void icmp6_error __P((struct mbuf *, int, int, int)); -int icmp6_input __P((struct mbuf **, int *, int)); -void icmp6_fasttimo __P((void)); -void icmp6_reflect __P((struct mbuf *, size_t)); -void icmp6_prepare __P((struct mbuf *)); -void icmp6_redirect_input __P((struct mbuf *, int)); -void icmp6_redirect_output __P((struct mbuf *, struct rtentry *)); - -/* XXX: is this the right place for these macros? */ -#define icmp6_ifstat_inc(ifp, tag) \ -do { \ - if ((ifp) && (ifp)->if_index <= if_index \ - && (ifp)->if_index < icmp6_ifstatmax \ - && icmp6_ifstat && icmp6_ifstat[(ifp)->if_index]) { \ - icmp6_ifstat[(ifp)->if_index]->tag++; \ - } \ -} while (0) - -#define icmp6_ifoutstat_inc(ifp, type, code) \ -do { \ - icmp6_ifstat_inc(ifp, ifs6_out_msg); \ - if (type < ICMP6_INFOMSG_MASK) \ - icmp6_ifstat_inc(ifp, ifs6_out_error); \ - switch(type) { \ - case ICMP6_DST_UNREACH: \ - icmp6_ifstat_inc(ifp, ifs6_out_dstunreach); \ - if (code == ICMP6_DST_UNREACH_ADMIN) \ - icmp6_ifstat_inc(ifp, ifs6_out_adminprohib); \ - break; \ - case ICMP6_PACKET_TOO_BIG: \ - icmp6_ifstat_inc(ifp, ifs6_out_pkttoobig); \ - break; \ - case ICMP6_TIME_EXCEEDED: \ - icmp6_ifstat_inc(ifp, ifs6_out_timeexceed); \ - break; \ - case ICMP6_PARAM_PROB: \ - icmp6_ifstat_inc(ifp, ifs6_out_paramprob); \ - break; \ - case ICMP6_ECHO_REQUEST: \ - icmp6_ifstat_inc(ifp, ifs6_out_echo); \ - break; \ - case ICMP6_ECHO_REPLY: \ - icmp6_ifstat_inc(ifp, ifs6_out_echoreply); \ - break; \ - case MLD6_LISTENER_QUERY: \ - icmp6_ifstat_inc(ifp, ifs6_out_mldquery); \ - break; \ - case MLD6_LISTENER_REPORT: \ - icmp6_ifstat_inc(ifp, ifs6_out_mldreport); \ - break; \ - case MLD6_LISTENER_DONE: \ - icmp6_ifstat_inc(ifp, ifs6_out_mlddone); \ - break; \ - case ND_ROUTER_SOLICIT: \ - icmp6_ifstat_inc(ifp, ifs6_out_routersolicit); \ - break; \ - case ND_ROUTER_ADVERT: \ - icmp6_ifstat_inc(ifp, ifs6_out_routeradvert); \ - break; \ - case ND_NEIGHBOR_SOLICIT: \ - icmp6_ifstat_inc(ifp, ifs6_out_neighborsolicit); \ - break; \ - case ND_NEIGHBOR_ADVERT: \ - icmp6_ifstat_inc(ifp, ifs6_out_neighboradvert); \ - break; \ - case ND_REDIRECT: \ - icmp6_ifstat_inc(ifp, ifs6_out_redirect); \ - break; \ - } \ -} while (0) - -extern int icmp6_rediraccept; /* accept/process redirects */ -extern int icmp6_redirtimeout; /* cache time for redirect routes */ -#endif /* _KERNEL */ - -#endif /* not _NETINET6_ICMPV6_H_ */ - +#error "netinet6/icmp6.h is obsolete. use netinet/icmp6.h" Index: head/sys/netinet6/in6.c =================================================================== --- head/sys/netinet6/in6.c (revision 62586) +++ head/sys/netinet6/in6.c (revision 62587) @@ -1,1899 +1,2013 @@ +/* $FreeBSD$ */ +/* $KAME: in6.c,v 1.87 2000/07/03 15:44:21 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* * Copyright (c) 1982, 1986, 1991, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)in.c 8.2 (Berkeley) 11/15/93 */ +#include "opt_inet.h" +#include "opt_inet6.h" + #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include "gif.h" - #include #include #include #include #include -#include +#include #include #include +#include #include +#include + +#include "gif.h" #if NGIF > 0 #include #endif #include MALLOC_DEFINE(M_IPMADDR, "in6_multi", "internet multicast address"); /* * Definitions of some costant IP6 addresses. */ -const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT; -const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT; -const struct in6_addr in6addr_nodelocal_allnodes = +const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT; +const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT; +const struct in6_addr in6addr_nodelocal_allnodes = IN6ADDR_NODELOCAL_ALLNODES_INIT; -const struct in6_addr in6addr_linklocal_allnodes = +const struct in6_addr in6addr_linklocal_allnodes = IN6ADDR_LINKLOCAL_ALLNODES_INIT; -const struct in6_addr in6addr_linklocal_allrouters = +const struct in6_addr in6addr_linklocal_allrouters = IN6ADDR_LINKLOCAL_ALLROUTERS_INIT; -const struct in6_addr in6mask0 = IN6MASK0; -const struct in6_addr in6mask32 = IN6MASK32; -const struct in6_addr in6mask64 = IN6MASK64; -const struct in6_addr in6mask96 = IN6MASK96; -const struct in6_addr in6mask128 = IN6MASK128; +const struct in6_addr in6mask0 = IN6MASK0; +const struct in6_addr in6mask32 = IN6MASK32; +const struct in6_addr in6mask64 = IN6MASK64; +const struct in6_addr in6mask96 = IN6MASK96; +const struct in6_addr in6mask128 = IN6MASK128; -static int in6_lifaddr_ioctl __P((struct socket *, u_long, caddr_t, +static int in6_lifaddr_ioctl __P((struct socket *, u_long, caddr_t, struct ifnet *, struct proc *)); -struct in6_multihead in6_multihead; /* XXX BSS initialization */ -/* - * Determine whether an IP6 address is in a reserved set of addresses - * that may not be forwarded, or whether datagrams to that destination - * may be forwarded. - */ -int -in6_canforward(src, dst) - struct in6_addr *src, *dst; -{ - if (IN6_IS_ADDR_LINKLOCAL(src) || - IN6_IS_ADDR_LINKLOCAL(dst) || - IN6_IS_ADDR_MULTICAST(dst)) - return(0); - return(1); -} +struct in6_multihead in6_multihead; /* XXX BSS initialization */ /* * Check if the loopback entry will be automatically generated. * if 0 returned, will not be automatically generated. * if 1 returned, will be automatically generated. */ static int in6_is_ifloop_auto(struct ifaddr *ifa) { #define SIN6(s) ((struct sockaddr_in6 *)s) /* * If RTF_CLONING is unset, or (IFF_LOOPBACK | IFF_POINTOPOINT), * or netmask is all0 or all1, then cloning will not happen, * then we can't rely on its loopback entry generation. */ if ((ifa->ifa_flags & RTF_CLONING) == 0 || (ifa->ifa_ifp->if_flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) || (SIN6(ifa->ifa_netmask)->sin6_len == sizeof(struct sockaddr_in6) && IN6_ARE_ADDR_EQUAL(&SIN6(ifa->ifa_netmask)->sin6_addr, &in6mask128)) || ((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_len == 0) return 0; else return 1; #undef SIN6 } /* * Subroutine for in6_ifaddloop() and in6_ifremloop(). * This routine does actual work. */ static void in6_ifloop_request(int cmd, struct ifaddr *ifa) { struct sockaddr_in6 lo_sa; struct sockaddr_in6 all1_sa; struct rtentry *nrt = NULL; bzero(&lo_sa, sizeof(lo_sa)); bzero(&all1_sa, sizeof(all1_sa)); lo_sa.sin6_family = AF_INET6; lo_sa.sin6_len = sizeof(struct sockaddr_in6); all1_sa = lo_sa; lo_sa.sin6_addr = in6addr_loopback; all1_sa.sin6_addr = in6mask128; - /* So we add or remove static loopback entry, here. */ + /* + * So we add or remove static loopback entry, here. + * This request for deletion could fail, e.g. when we remove + * an address right after adding it. + */ rtrequest(cmd, ifa->ifa_addr, (struct sockaddr *)&lo_sa, (struct sockaddr *)&all1_sa, RTF_UP|RTF_HOST, &nrt); /* * Make sure rt_ifa be equal to IFA, the second argument of the * function. * We need this because when we refer rt_ifa->ia6_flags in ip6_input, * we assume that the rt_ifa points to the address instead of the * loopback address. */ if (cmd == RTM_ADD && nrt && ifa != nrt->rt_ifa) { - nrt->rt_ifa->ifa_refcnt--; + IFAFREE(nrt->rt_ifa); ifa->ifa_refcnt++; nrt->rt_ifa = ifa; } if (nrt) nrt->rt_refcnt--; } /* * Add ownaddr as loopback rtentry, if necessary(ex. on p2p link). * Because, KAME needs loopback rtentry for ownaddr check in * ip6_input(). */ static void in6_ifaddloop(struct ifaddr *ifa) { if (!in6_is_ifloop_auto(ifa)) { struct rtentry *rt; /* If there is no loopback entry, allocate one. */ rt = rtalloc1(ifa->ifa_addr, 0, 0); if (rt == 0 || (rt->rt_ifp->if_flags & IFF_LOOPBACK) == 0) in6_ifloop_request(RTM_ADD, ifa); if (rt) rt->rt_refcnt--; } } /* * Remove loopback rtentry of ownaddr generated by in6_ifaddloop(), * if it exists. */ static void in6_ifremloop(struct ifaddr *ifa) { - if (!in6_is_ifloop_auto(ifa)) { - struct in6_ifaddr *ia; - int ia_count = 0; + struct in6_ifaddr *ia; + int ia_count = 0; + /* + * All BSD variants except BSD/OS do not remove cloned routes + * from an interface direct route, when removing the direct route + * (see commens in net/net_osdep.h). + * So we should remove the route corresponding to the deleted address + * regardless of the result of in6_is_ifloop_auto(). + */ + if (1) + { /* If only one ifa for the loopback entry, delete it. */ for (ia = in6_ifaddr; ia; ia = ia->ia_next) { if (IN6_ARE_ADDR_EQUAL(IFA_IN6(ifa), &ia->ia_addr.sin6_addr)) { ia_count++; if (ia_count > 1) break; } } if (ia_count == 1) in6_ifloop_request(RTM_DELETE, ifa); } } -/* - * Subroutine for in6_ifaddproxy() and in6_ifremproxy(). - * This routine does actual work. - * call in6_addmulti() when cmd == 1. - * call in6_delmulti() when cmd == 2. - */ -static int -in6_ifproxy_request(int cmd, struct in6_ifaddr *ia) -{ - int error = 0; - - /* - * If we have an IPv6 dstaddr on adding p2p interface, - * join dstaddr's solicited multicast on necessary interface. - */ - if ((ia->ia_ifp->if_flags & IFF_POINTOPOINT) && - ia->ia_dstaddr.sin6_family == AF_INET6 && - !IN6_IS_ADDR_LINKLOCAL(&ia->ia_dstaddr.sin6_addr)) { - struct in6_ifaddr *ia_lan; - - /* - * TODO: Join only on some specified interfaces by some - * configuration. - * Unsolicited Neighbor Advertisements will be also necessary. - * - * Now, join on interfaces which meets following. - * -IFF_BROADCAST and IFF_MULTICAST - * (NBMA is out of scope) - * -the prefix value is same as p2p dstaddr - */ - for (ia_lan = in6_ifaddr; ia_lan; ia_lan = ia_lan->ia_next) { - struct in6_addr llsol; - - if ((ia_lan->ia_ifp->if_flags & - (IFF_BROADCAST|IFF_MULTICAST)) != - (IFF_BROADCAST|IFF_MULTICAST)) - continue; - if (!IN6_ARE_MASKED_ADDR_EQUAL(IA6_IN6(ia), - IA6_IN6(ia_lan), - IA6_MASKIN6(ia_lan))) - continue; - if (ia_lan->ia_ifp == ia->ia_ifp) - continue; - - /* init llsol */ - bzero(&llsol, sizeof(struct in6_addr)); - llsol.s6_addr16[0] = htons(0xff02); - llsol.s6_addr16[1] = htons(ia_lan->ia_ifp->if_index); - llsol.s6_addr32[1] = 0; - llsol.s6_addr32[2] = htonl(1); - llsol.s6_addr32[3] = - ia->ia_dstaddr.sin6_addr.s6_addr32[3]; - llsol.s6_addr8[12] = 0xff; - - if (cmd == 1) - (void)in6_addmulti(&llsol, - ia_lan->ia_ifp, - &error); - else if (cmd == 2) { - struct in6_multi *in6m; - - IN6_LOOKUP_MULTI(llsol, - ia_lan->ia_ifp, - in6m); - if (in6m) - in6_delmulti(in6m); - } - } - } - return error; -} - -static int -in6_ifaddproxy(struct in6_ifaddr *ia) -{ - return(in6_ifproxy_request(1, ia)); -} - -static void -in6_ifremproxy(struct in6_ifaddr *ia) -{ - in6_ifproxy_request(2, ia); -} - int in6_ifindex2scopeid(idx) int idx; { struct ifnet *ifp; struct ifaddr *ifa; struct sockaddr_in6 *sin6; if (idx < 0 || if_index < idx) return -1; ifp = ifindex2ifnet[idx]; TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; sin6 = (struct sockaddr_in6 *)ifa->ifa_addr; if (IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr)) return sin6->sin6_scope_id & 0xffff; } return -1; } int in6_mask2len(mask) struct in6_addr *mask; { int x, y; for (x = 0; x < sizeof(*mask); x++) { if (mask->s6_addr8[x] != 0xff) break; } y = 0; if (x < sizeof(*mask)) { for (y = 0; y < 8; y++) { if ((mask->s6_addr8[x] & (0x80 >> y)) == 0) break; } } return x * 8 + y; } void in6_len2mask(mask, len) struct in6_addr *mask; int len; { int i; bzero(mask, sizeof(*mask)); for (i = 0; i < len / 8; i++) mask->s6_addr8[i] = 0xff; if (len % 8) mask->s6_addr8[i] = (0xff00 >> (len % 8)) & 0xff; } -int in6_interfaces; /* number of external internet interfaces */ - #define ifa2ia6(ifa) ((struct in6_ifaddr *)(ifa)) -#define ia62ifa(ia6) ((struct ifaddr *)(ia6)) +#define ia62ifa(ia6) (&((ia6)->ia_ifa)) int in6_control(so, cmd, data, ifp, p) struct socket *so; u_long cmd; caddr_t data; struct ifnet *ifp; struct proc *p; { struct in6_ifreq *ifr = (struct in6_ifreq *)data; - struct in6_ifaddr *ia, *oia; + struct in6_ifaddr *ia = NULL, *oia; struct in6_aliasreq *ifra = (struct in6_aliasreq *)data; - struct sockaddr_in6 oldaddr, net; - int error = 0, hostIsNew, prefixIsNew; + struct sockaddr_in6 oldaddr; +#ifdef COMPAT_IN6IFIOCTL + struct sockaddr_in6 net; +#endif + int error = 0, hostIsNew, prefixIsNew; + int newifaddr; int privileged; privileged = 0; if (p == NULL || !suser(p)) privileged++; /* * xxx should prevent processes for link-local addresses? */ #if NGIF > 0 if (ifp && ifp->if_type == IFT_GIF) { switch (cmd) { case SIOCSIFPHYADDR_IN6: if (!privileged) return(EPERM); /*fall through*/ case SIOCGIFPSRCADDR_IN6: case SIOCGIFPDSTADDR_IN6: return gif_ioctl(ifp, cmd, data); } } #endif + switch (cmd) { + case SIOCGETSGCNT_IN6: + case SIOCGETMIFCNT_IN6: + return (mrt6_ioctl(cmd, data)); + } - if (ifp == 0) + if (ifp == NULL) return(EOPNOTSUPP); switch (cmd) { case SIOCSNDFLUSH_IN6: case SIOCSPFXFLUSH_IN6: case SIOCSRTRFLUSH_IN6: + case SIOCSDEFIFACE_IN6: + case SIOCSIFINFO_FLAGS: if (!privileged) return(EPERM); /*fall through*/ case SIOCGIFINFO_IN6: case SIOCGDRLST_IN6: case SIOCGPRLST_IN6: case SIOCGNBRINFO_IN6: + case SIOCGDEFIFACE_IN6: return(nd6_ioctl(cmd, data, ifp)); } switch (cmd) { case SIOCSIFPREFIX_IN6: case SIOCDIFPREFIX_IN6: case SIOCAIFPREFIX_IN6: case SIOCCIFPREFIX_IN6: case SIOCSGIFPREFIX_IN6: if (!privileged) return(EPERM); /*fall through*/ case SIOCGIFPREFIX_IN6: if (ip6_forwarding == 0) return(EPERM); return(in6_prefix_ioctl(so, cmd, data, ifp)); } + switch(cmd) { + case SIOCSSCOPE6: + if (!privileged) + return(EPERM); + return(scope6_set(ifp, ifr->ifr_ifru.ifru_scope_id)); + break; + case SIOCGSCOPE6: + return(scope6_get(ifp, ifr->ifr_ifru.ifru_scope_id)); + break; + case SIOCGSCOPE6DEF: + return(scope6_get_default(ifr->ifr_ifru.ifru_scope_id)); + break; + } + switch (cmd) { case SIOCALIFADDR: case SIOCDLIFADDR: if (!privileged) return(EPERM); /*fall through*/ case SIOCGLIFADDR: return in6_lifaddr_ioctl(so, cmd, data, ifp, p); } /* * Find address for this interface, if it exists. */ - { - + if (ifra->ifra_addr.sin6_family == AF_INET6) { /* XXX */ struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)&ifra->ifra_addr; if (IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr)) { if (sa6->sin6_addr.s6_addr16[1] == 0) { /* interface ID is not embedded by the user */ sa6->sin6_addr.s6_addr16[1] = htons(ifp->if_index); - } else - if (sa6->sin6_addr.s6_addr16[1] != - htons(ifp->if_index)) - return(EINVAL); /* ifid is contradict */ + } else if (sa6->sin6_addr.s6_addr16[1] != + htons(ifp->if_index)) { + return(EINVAL); /* ifid is contradict */ + } if (sa6->sin6_scope_id) { if (sa6->sin6_scope_id != (u_int32_t)ifp->if_index) return(EINVAL); sa6->sin6_scope_id = 0; /* XXX: good way? */ } } + ia = in6ifa_ifpwithaddr(ifp, &ifra->ifra_addr.sin6_addr); } - ia = in6ifa_ifpwithaddr(ifp, &ifra->ifra_addr.sin6_addr); switch (cmd) { case SIOCDIFADDR_IN6: - if (ia == 0) + /* + * for IPv4, we look for existing in6_ifaddr here to allow + * "ifconfig if0 delete" to remove first IPv4 address on the + * interface. For IPv6, as the spec allow multiple interface + * address from the day one, we consider "remove the first one" + * semantics to be not preferrable. + */ + if (ia == NULL) return(EADDRNOTAVAIL); /* FALLTHROUGH */ case SIOCAIFADDR_IN6: case SIOCSIFADDR_IN6: - case SIOCSIFNETMASK_IN6: +#ifdef COMPAT_IN6IFIOCTL case SIOCSIFDSTADDR_IN6: + case SIOCSIFNETMASK_IN6: + /* + * Since IPv6 allows a node to assign multiple addresses + * on a single interface, SIOCSIFxxx ioctls are not suitable + * and should be unused. + */ +#endif + if (ifra->ifra_addr.sin6_family != AF_INET6) + return(EAFNOSUPPORT); if (!privileged) return(EPERM); - if (ia == 0) { + if (ia == NULL) { ia = (struct in6_ifaddr *) malloc(sizeof(*ia), M_IFADDR, M_WAITOK); if (ia == NULL) return (ENOBUFS); bzero((caddr_t)ia, sizeof(*ia)); + /* Initialize the address and masks */ ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr; - ia->ia_ifa.ifa_dstaddr - = (struct sockaddr *)&ia->ia_dstaddr; + ia->ia_addr.sin6_family = AF_INET6; + ia->ia_addr.sin6_len = sizeof(ia->ia_addr); +#if 1 + if (ifp->if_flags & IFF_POINTOPOINT) { + ia->ia_ifa.ifa_dstaddr + = (struct sockaddr *)&ia->ia_dstaddr; + ia->ia_dstaddr.sin6_family = AF_INET6; + ia->ia_dstaddr.sin6_len = sizeof(ia->ia_dstaddr); + } else { + ia->ia_ifa.ifa_dstaddr = NULL; + bzero(&ia->ia_dstaddr, sizeof(ia->ia_dstaddr)); + } +#else /* always initilize by NULL */ + ia->ia_ifa.ifa_dstaddr = NULL; + bzero(&ia->ia_dstaddr, sizeof(ia->ia_dstaddr)); +#endif ia->ia_ifa.ifa_netmask = (struct sockaddr *)&ia->ia_prefixmask; ia->ia_ifp = ifp; if ((oia = in6_ifaddr) != NULL) { for ( ; oia->ia_next; oia = oia->ia_next) continue; oia->ia_next = ia; } else in6_ifaddr = ia; - TAILQ_INSERT_TAIL(&ifp->if_addrlist, - (struct ifaddr *)ia, ifa_list); - if ((ifp->if_flags & IFF_LOOPBACK) == 0) - in6_interfaces++; /*XXX*/ - } + /* gain a refcnt for the link from in6_ifaddr */ + ia->ia_ifa.ifa_refcnt++; + TAILQ_INSERT_TAIL(&ifp->if_addrlist, &ia->ia_ifa, + ifa_list); + /* gain another refcnt for the link from if_addrlist */ + ia->ia_ifa.ifa_refcnt++; + + newifaddr = 1; + } else + newifaddr = 0; + if (cmd == SIOCAIFADDR_IN6) { /* sanity for overflow - beware unsigned */ struct in6_addrlifetime *lt; lt = &ifra->ifra_lifetime; if (lt->ia6t_vltime != ND6_INFINITE_LIFETIME && lt->ia6t_vltime + time_second < time_second) { return EINVAL; } if (lt->ia6t_pltime != ND6_INFINITE_LIFETIME && lt->ia6t_pltime + time_second < time_second) { return EINVAL; } } break; case SIOCGIFADDR_IN6: /* This interface is basically deprecated. use SIOCGIFCONF. */ /* fall through */ case SIOCGIFAFLAG_IN6: case SIOCGIFNETMASK_IN6: case SIOCGIFDSTADDR_IN6: case SIOCGIFALIFETIME_IN6: /* must think again about its semantics */ - if (ia == 0) + if (ia == NULL) return(EADDRNOTAVAIL); break; case SIOCSIFALIFETIME_IN6: { struct in6_addrlifetime *lt; if (!privileged) return(EPERM); - if (ia == 0) + if (ia == NULL) return(EADDRNOTAVAIL); /* sanity for overflow - beware unsigned */ lt = &ifr->ifr_ifru.ifru_lifetime; if (lt->ia6t_vltime != ND6_INFINITE_LIFETIME && lt->ia6t_vltime + time_second < time_second) { return EINVAL; } if (lt->ia6t_pltime != ND6_INFINITE_LIFETIME && lt->ia6t_pltime + time_second < time_second) { return EINVAL; } break; } } switch (cmd) { case SIOCGIFADDR_IN6: ifr->ifr_addr = ia->ia_addr; break; case SIOCGIFDSTADDR_IN6: if ((ifp->if_flags & IFF_POINTOPOINT) == 0) return(EINVAL); + /* + * XXX: should we check if ifa_dstaddr is NULL and return + * an error? + */ ifr->ifr_dstaddr = ia->ia_dstaddr; break; case SIOCGIFNETMASK_IN6: ifr->ifr_addr = ia->ia_prefixmask; break; case SIOCGIFAFLAG_IN6: ifr->ifr_ifru.ifru_flags6 = ia->ia6_flags; break; case SIOCGIFSTAT_IN6: if (ifp == NULL) return EINVAL; if (in6_ifstat == NULL || ifp->if_index >= in6_ifstatmax || in6_ifstat[ifp->if_index] == NULL) { /* return EAFNOSUPPORT? */ bzero(&ifr->ifr_ifru.ifru_stat, sizeof(ifr->ifr_ifru.ifru_stat)); } else ifr->ifr_ifru.ifru_stat = *in6_ifstat[ifp->if_index]; break; case SIOCGIFSTAT_ICMP6: if (ifp == NULL) return EINVAL; if (icmp6_ifstat == NULL || ifp->if_index >= icmp6_ifstatmax || icmp6_ifstat[ifp->if_index] == NULL) { /* return EAFNOSUPPORT? */ bzero(&ifr->ifr_ifru.ifru_stat, sizeof(ifr->ifr_ifru.ifru_icmp6stat)); } else ifr->ifr_ifru.ifru_icmp6stat = *icmp6_ifstat[ifp->if_index]; break; +#ifdef COMPAT_IN6IFIOCTL /* should be unused */ case SIOCSIFDSTADDR_IN6: if ((ifp->if_flags & IFF_POINTOPOINT) == 0) return(EINVAL); oldaddr = ia->ia_dstaddr; ia->ia_dstaddr = ifr->ifr_dstaddr; /* link-local index check */ if (IN6_IS_ADDR_LINKLOCAL(&ia->ia_dstaddr.sin6_addr)) { if (ia->ia_dstaddr.sin6_addr.s6_addr16[1] == 0) { /* interface ID is not embedded by the user */ ia->ia_dstaddr.sin6_addr.s6_addr16[1] = htons(ifp->if_index); - } else - if (ia->ia_dstaddr.sin6_addr.s6_addr16[1] != + } else if (ia->ia_dstaddr.sin6_addr.s6_addr16[1] != htons(ifp->if_index)) { - ia->ia_dstaddr = oldaddr; - return(EINVAL); /* ifid is contradict */ - } + ia->ia_dstaddr = oldaddr; + return(EINVAL); /* ifid is contradict */ + } } if (ifp->if_ioctl && (error = (ifp->if_ioctl) (ifp, SIOCSIFDSTADDR, (caddr_t)ia))) { ia->ia_dstaddr = oldaddr; return(error); } + ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr; if (ia->ia_flags & IFA_ROUTE) { ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&oldaddr; rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST); ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr; rtinit(&(ia->ia_ifa), (int)RTM_ADD, RTF_HOST|RTF_UP); } break; +#endif case SIOCGIFALIFETIME_IN6: ifr->ifr_ifru.ifru_lifetime = ia->ia6_lifetime; break; case SIOCSIFALIFETIME_IN6: ia->ia6_lifetime = ifr->ifr_ifru.ifru_lifetime; /* for sanity */ if (ia->ia6_lifetime.ia6t_vltime != ND6_INFINITE_LIFETIME) { ia->ia6_lifetime.ia6t_expire = time_second + ia->ia6_lifetime.ia6t_vltime; } else ia->ia6_lifetime.ia6t_expire = 0; if (ia->ia6_lifetime.ia6t_pltime != ND6_INFINITE_LIFETIME) { ia->ia6_lifetime.ia6t_preferred = time_second + ia->ia6_lifetime.ia6t_pltime; } else ia->ia6_lifetime.ia6t_preferred = 0; break; case SIOCSIFADDR_IN6: - return(in6_ifinit(ifp, ia, &ifr->ifr_addr, 1)); + error = in6_ifinit(ifp, ia, &ifr->ifr_addr, 1); +#if 0 + /* + * the code chokes if we are to assign multiple addresses with + * the same address prefix (rtinit() will return EEXIST, which + * is not fatal actually). we will get memory leak if we + * don't do it. + * -> we may want to hide EEXIST from rtinit(). + */ + undo: + if (error && newifaddr) { + TAILQ_REMOVE(&ifp->if_addrlist, &ia->ia_ifa, ifa_list); + /* release a refcnt for the link from if_addrlist */ + IFAFREE(&ia->ia_ifa); + oia = ia; + if (oia == (ia = in6_ifaddr)) + in6_ifaddr = ia->ia_next; + else { + while (ia->ia_next && (ia->ia_next != oia)) + ia = ia->ia_next; + if (ia->ia_next) + ia->ia_next = oia->ia_next; + else { + printf("Didn't unlink in6_ifaddr " + "from list\n"); + } + } + /* release another refcnt for the link from in6_ifaddr */ + IFAFREE(&oia->ia_ifa); + } +#endif + return error; + +#ifdef COMPAT_IN6IFIOCTL /* XXX should be unused */ case SIOCSIFNETMASK_IN6: ia->ia_prefixmask = ifr->ifr_addr; bzero(&net, sizeof(net)); net.sin6_len = sizeof(struct sockaddr_in6); net.sin6_family = AF_INET6; net.sin6_port = htons(0); net.sin6_flowinfo = htonl(0); net.sin6_addr.s6_addr32[0] = ia->ia_addr.sin6_addr.s6_addr32[0] & ia->ia_prefixmask.sin6_addr.s6_addr32[0]; net.sin6_addr.s6_addr32[1] = ia->ia_addr.sin6_addr.s6_addr32[1] & ia->ia_prefixmask.sin6_addr.s6_addr32[1]; net.sin6_addr.s6_addr32[2] = ia->ia_addr.sin6_addr.s6_addr32[2] & ia->ia_prefixmask.sin6_addr.s6_addr32[2]; net.sin6_addr.s6_addr32[3] = ia->ia_addr.sin6_addr.s6_addr32[3] & ia->ia_prefixmask.sin6_addr.s6_addr32[3]; ia->ia_net = net; break; +#endif case SIOCAIFADDR_IN6: prefixIsNew = 0; hostIsNew = 1; if (ifra->ifra_addr.sin6_len == 0) { ifra->ifra_addr = ia->ia_addr; hostIsNew = 0; } else if (IN6_ARE_ADDR_EQUAL(&ifra->ifra_addr.sin6_addr, &ia->ia_addr.sin6_addr)) hostIsNew = 0; + /* Validate address families: */ + /* + * The destination address for a p2p link must have a family + * of AF_UNSPEC or AF_INET6. + */ + if ((ifp->if_flags & IFF_POINTOPOINT) != 0 && + ifra->ifra_dstaddr.sin6_family != AF_INET6 && + ifra->ifra_dstaddr.sin6_family != AF_UNSPEC) + return(EAFNOSUPPORT); + /* + * The prefixmask must have a family of AF_UNSPEC or AF_INET6. + */ + if (ifra->ifra_prefixmask.sin6_family != AF_INET6 && + ifra->ifra_prefixmask.sin6_family != AF_UNSPEC) + return(EAFNOSUPPORT); + if (ifra->ifra_prefixmask.sin6_len) { in6_ifscrub(ifp, ia); ia->ia_prefixmask = ifra->ifra_prefixmask; prefixIsNew = 1; } if ((ifp->if_flags & IFF_POINTOPOINT) && (ifra->ifra_dstaddr.sin6_family == AF_INET6)) { in6_ifscrub(ifp, ia); + oldaddr = ia->ia_dstaddr; ia->ia_dstaddr = ifra->ifra_dstaddr; /* link-local index check: should be a separate function? */ if (IN6_IS_ADDR_LINKLOCAL(&ia->ia_dstaddr.sin6_addr)) { if (ia->ia_dstaddr.sin6_addr.s6_addr16[1] == 0) { /* * interface ID is not embedded by * the user */ ia->ia_dstaddr.sin6_addr.s6_addr16[1] = htons(ifp->if_index); - } else - if (ia->ia_dstaddr.sin6_addr.s6_addr16[1] != + } else if (ia->ia_dstaddr.sin6_addr.s6_addr16[1] != htons(ifp->if_index)) { - ia->ia_dstaddr = oldaddr; - return(EINVAL); /* ifid is contradict */ - } + ia->ia_dstaddr = oldaddr; + return(EINVAL); /* ifid is contradict */ + } } prefixIsNew = 1; /* We lie; but effect's the same */ } - if (ifra->ifra_addr.sin6_family == AF_INET6 && - (hostIsNew || prefixIsNew)) + if (hostIsNew || prefixIsNew) { error = in6_ifinit(ifp, ia, &ifra->ifra_addr, 0); - if (ifra->ifra_addr.sin6_family == AF_INET6 - && hostIsNew && (ifp->if_flags & IFF_MULTICAST)) { +#if 0 + if (error) + goto undo; +#endif + } + if (hostIsNew && (ifp->if_flags & IFF_MULTICAST)) { int error_local = 0; /* * join solicited multicast addr for new host id */ struct in6_addr llsol; bzero(&llsol, sizeof(struct in6_addr)); llsol.s6_addr16[0] = htons(0xff02); llsol.s6_addr16[1] = htons(ifp->if_index); llsol.s6_addr32[1] = 0; llsol.s6_addr32[2] = htonl(1); llsol.s6_addr32[3] = ifra->ifra_addr.sin6_addr.s6_addr32[3]; llsol.s6_addr8[12] = 0xff; (void)in6_addmulti(&llsol, ifp, &error_local); if (error == 0) error = error_local; } - /* Join dstaddr's solicited multicast if necessary. */ - if (nd6_proxyall && hostIsNew) { - int error_local; - error_local = in6_ifaddproxy(ia); - if (error == 0) - error = error_local; - } - ia->ia6_flags = ifra->ifra_flags; ia->ia6_flags &= ~IN6_IFF_DUPLICATED; /*safety*/ ia->ia6_lifetime = ifra->ifra_lifetime; /* for sanity */ if (ia->ia6_lifetime.ia6t_vltime != ND6_INFINITE_LIFETIME) { ia->ia6_lifetime.ia6t_expire = time_second + ia->ia6_lifetime.ia6t_vltime; } else ia->ia6_lifetime.ia6t_expire = 0; if (ia->ia6_lifetime.ia6t_pltime != ND6_INFINITE_LIFETIME) { ia->ia6_lifetime.ia6t_preferred = time_second + ia->ia6_lifetime.ia6t_pltime; } else ia->ia6_lifetime.ia6t_preferred = 0; /* * Perform DAD, if needed. * XXX It may be of use, if we can administratively * disable DAD. */ switch (ifp->if_type) { case IFT_ARCNET: case IFT_ETHER: case IFT_FDDI: - ia->ia6_flags |= IN6_IFF_TENTATIVE; - nd6_dad_start((struct ifaddr *)ia, NULL); +#if 0 + case IFT_ATM: + case IFT_SLIP: + case IFT_PPP: +#endif + { + ia->ia6_flags |= IN6_IFF_TENTATIVE; + nd6_dad_start((struct ifaddr *)ia, NULL); + } break; #ifdef IFT_DUMMY case IFT_DUMMY: #endif case IFT_FAITH: case IFT_GIF: case IFT_LOOP: default: break; } if (hostIsNew) { int iilen; int error_local = 0; iilen = (sizeof(ia->ia_prefixmask.sin6_addr) << 3) - in6_mask2len(&ia->ia_prefixmask.sin6_addr); error_local = in6_prefix_add_ifid(iilen, ia); if (error == 0) error = error_local; } return(error); case SIOCDIFADDR_IN6: - in6_ifscrub(ifp, ia); - - if (ifp->if_flags & IFF_MULTICAST) { - /* - * delete solicited multicast addr for deleting host id - */ - struct in6_multi *in6m; - struct in6_addr llsol; - bzero(&llsol, sizeof(struct in6_addr)); - llsol.s6_addr16[0] = htons(0xff02); - llsol.s6_addr16[1] = htons(ifp->if_index); - llsol.s6_addr32[1] = 0; - llsol.s6_addr32[2] = htonl(1); - llsol.s6_addr32[3] = - ia->ia_addr.sin6_addr.s6_addr32[3]; - llsol.s6_addr8[12] = 0xff; - - IN6_LOOKUP_MULTI(llsol, ifp, in6m); - if (in6m) - in6_delmulti(in6m); - } - /* Leave dstaddr's solicited multicast if necessary. */ - if (nd6_proxyall) - in6_ifremproxy(ia); - - TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list); - oia = ia; - if (oia == (ia = in6_ifaddr)) - in6_ifaddr = ia->ia_next; - else { - while (ia->ia_next && (ia->ia_next != oia)) - ia = ia->ia_next; - if (ia->ia_next) - ia->ia_next = oia->ia_next; - else - printf("Didn't unlink in6_ifaddr from list\n"); - } - { - int iilen; - - iilen = (sizeof(oia->ia_prefixmask.sin6_addr) << 3) - - in6_mask2len(&oia->ia_prefixmask.sin6_addr); - in6_prefix_remove_ifid(iilen, oia); - } - IFAFREE((&oia->ia_ifa)); + in6_purgeaddr(&ia->ia_ifa, ifp); break; default: - if (ifp == 0 || ifp->if_ioctl == 0) + if (ifp == NULL || ifp->if_ioctl == 0) return(EOPNOTSUPP); return((*ifp->if_ioctl)(ifp, cmd, data)); } return(0); } +void +in6_purgeaddr(ifa, ifp) + struct ifaddr *ifa; + struct ifnet *ifp; +{ + struct in6_ifaddr *oia, *ia = (void *) ifa; + int plen; + + in6_ifscrub(ifp, ia); + + if (ifp->if_flags & IFF_MULTICAST) { + /* + * delete solicited multicast addr for deleting host id + */ + struct in6_multi *in6m; + struct in6_addr llsol; + bzero(&llsol, sizeof(struct in6_addr)); + llsol.s6_addr16[0] = htons(0xff02); + llsol.s6_addr16[1] = htons(ifp->if_index); + llsol.s6_addr32[1] = 0; + llsol.s6_addr32[2] = htonl(1); + llsol.s6_addr32[3] = + ia->ia_addr.sin6_addr.s6_addr32[3]; + llsol.s6_addr8[12] = 0xff; + + IN6_LOOKUP_MULTI(llsol, ifp, in6m); + if (in6m) + in6_delmulti(in6m); + } + + TAILQ_REMOVE(&ifp->if_addrlist, &ia->ia_ifa, ifa_list); + /* release a refcnt for the link from if_addrlist */ + IFAFREE(&ia->ia_ifa); + + oia = ia; + if (oia == (ia = in6_ifaddr)) + in6_ifaddr = ia->ia_next; + else { + while (ia->ia_next && (ia->ia_next != oia)) + ia = ia->ia_next; + if (ia->ia_next) + ia->ia_next = oia->ia_next; + else + printf("Didn't unlink in6_ifaddr from list\n"); + } + { + int iilen; + + plen = in6_mask2len(&oia->ia_prefixmask.sin6_addr); + iilen = (sizeof(oia->ia_prefixmask.sin6_addr) << 3) - plen; + in6_prefix_remove_ifid(iilen, oia); + } + + /* + * Check if we have another address that has the same prefix of + * the purged address. If we have one, reinstall the corresponding + * interface route. + */ + for (ia = in6_ifaddr; ia; ia = ia->ia_next) { + int e; + + if (in6_are_prefix_equal(&ia->ia_addr.sin6_addr, + &oia->ia_addr.sin6_addr, plen)) { + if ((e = rtinit(&(ia->ia_ifa), (int)RTM_ADD, + ia->ia_flags)) == 0) { + ia->ia_flags |= IFA_ROUTE; + break; + } + else { + log(LOG_NOTICE, + "in6_purgeaddr: failed to add an interface" + " route for %s/%d on %s, errno = %d\n", + ip6_sprintf(&ia->ia_addr.sin6_addr), + plen, if_name(ia->ia_ifp), e); + /* still trying */ + } + } + } + + /* release another refcnt for the link from in6_ifaddr */ + IFAFREE(&oia->ia_ifa); +} + /* * SIOC[GAD]LIFADDR. - * SIOCGLIFADDR: get first address. (?!?) + * SIOCGLIFADDR: get first address. (???) * SIOCGLIFADDR with IFLR_PREFIX: * get first address that matches the specified prefix. * SIOCALIFADDR: add the specified address. * SIOCALIFADDR with IFLR_PREFIX: * add the specified prefix, filling hostid part from * the first link-local address. prefixlen must be <= 64. * SIOCDLIFADDR: delete the specified address. * SIOCDLIFADDR with IFLR_PREFIX: * delete the first address that matches the specified prefix. * return values: * EINVAL on invalid parameters * EADDRNOTAVAIL on prefix match failed/specified address not found * other values may be returned from in6_ioctl() * * NOTE: SIOCALIFADDR(with IFLR_PREFIX set) allows prefixlen less than 64. * this is to accomodate address naming scheme other than RFC2374, * in the future. * RFC2373 defines interface id to be 64bit, but it allows non-RFC2374 * address encoding scheme. (see figure on page 8) */ static int in6_lifaddr_ioctl(so, cmd, data, ifp, p) struct socket *so; u_long cmd; caddr_t data; struct ifnet *ifp; struct proc *p; { struct if_laddrreq *iflr = (struct if_laddrreq *)data; struct ifaddr *ifa; + struct sockaddr *sa; /* sanity checks */ if (!data || !ifp) { panic("invalid argument to in6_lifaddr_ioctl"); /*NOTRECHED*/ } switch (cmd) { case SIOCGLIFADDR: /* address must be specified on GET with IFLR_PREFIX */ if ((iflr->flags & IFLR_PREFIX) == 0) break; /*FALLTHROUGH*/ case SIOCALIFADDR: case SIOCDLIFADDR: /* address must be specified on ADD and DELETE */ - if (iflr->addr.ss_family != AF_INET6) + sa = (struct sockaddr *)&iflr->addr; + if (sa->sa_family != AF_INET6) return EINVAL; - if (iflr->addr.ss_len != sizeof(struct sockaddr_in6)) + if (sa->sa_len != sizeof(struct sockaddr_in6)) return EINVAL; /* XXX need improvement */ - if (iflr->dstaddr.ss_family - && iflr->dstaddr.ss_family != AF_INET6) + sa = (struct sockaddr *)&iflr->dstaddr; + if (sa->sa_family && sa->sa_family != AF_INET6) return EINVAL; - if (iflr->dstaddr.ss_family - && iflr->dstaddr.ss_len != sizeof(struct sockaddr_in6)) + if (sa->sa_len && sa->sa_len != sizeof(struct sockaddr_in6)) return EINVAL; break; default: /*shouldn't happen*/ +#if 0 + panic("invalid cmd to in6_lifaddr_ioctl"); + /*NOTREACHED*/ +#else return EOPNOTSUPP; +#endif } if (sizeof(struct in6_addr) * 8 < iflr->prefixlen) return EINVAL; switch (cmd) { case SIOCALIFADDR: { struct in6_aliasreq ifra; struct in6_addr *hostid = NULL; int prefixlen; if ((iflr->flags & IFLR_PREFIX) != 0) { struct sockaddr_in6 *sin6; /* * hostid is to fill in the hostid part of the * address. hostid points to the first link-local * address attached to the interface. */ - ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp); + ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp, 0); if (!ifa) return EADDRNOTAVAIL; hostid = IFA_IN6(ifa); /* prefixlen must be <= 64. */ if (64 < iflr->prefixlen) return EINVAL; prefixlen = iflr->prefixlen; /* hostid part must be zero. */ sin6 = (struct sockaddr_in6 *)&iflr->addr; if (sin6->sin6_addr.s6_addr32[2] != 0 || sin6->sin6_addr.s6_addr32[3] != 0) { return EINVAL; } } else prefixlen = iflr->prefixlen; /* copy args to in6_aliasreq, perform ioctl(SIOCAIFADDR_IN6). */ bzero(&ifra, sizeof(ifra)); bcopy(iflr->iflr_name, ifra.ifra_name, sizeof(ifra.ifra_name)); - bcopy(&iflr->addr, &ifra.ifra_addr, iflr->addr.ss_len); + bcopy(&iflr->addr, &ifra.ifra_addr, + ((struct sockaddr *)&iflr->addr)->sa_len); if (hostid) { /* fill in hostid part */ ifra.ifra_addr.sin6_addr.s6_addr32[2] = hostid->s6_addr32[2]; ifra.ifra_addr.sin6_addr.s6_addr32[3] = hostid->s6_addr32[3]; } - if (iflr->dstaddr.ss_family) { /*XXX*/ + if (((struct sockaddr *)&iflr->dstaddr)->sa_family) { /*XXX*/ bcopy(&iflr->dstaddr, &ifra.ifra_dstaddr, - iflr->dstaddr.ss_len); + ((struct sockaddr *)&iflr->dstaddr)->sa_len); if (hostid) { ifra.ifra_dstaddr.sin6_addr.s6_addr32[2] = hostid->s6_addr32[2]; ifra.ifra_dstaddr.sin6_addr.s6_addr32[3] = hostid->s6_addr32[3]; } } ifra.ifra_prefixmask.sin6_family = AF_INET6; ifra.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6); in6_len2mask(&ifra.ifra_prefixmask.sin6_addr, prefixlen); ifra.ifra_flags = iflr->flags & ~IFLR_PREFIX; return in6_control(so, SIOCAIFADDR_IN6, (caddr_t)&ifra, ifp, p); } case SIOCGLIFADDR: case SIOCDLIFADDR: { struct in6_ifaddr *ia; struct in6_addr mask, candidate, match; struct sockaddr_in6 *sin6; int cmp; bzero(&mask, sizeof(mask)); if (iflr->flags & IFLR_PREFIX) { /* lookup a prefix rather than address. */ in6_len2mask(&mask, iflr->prefixlen); sin6 = (struct sockaddr_in6 *)&iflr->addr; bcopy(&sin6->sin6_addr, &match, sizeof(match)); match.s6_addr32[0] &= mask.s6_addr32[0]; match.s6_addr32[1] &= mask.s6_addr32[1]; match.s6_addr32[2] &= mask.s6_addr32[2]; match.s6_addr32[3] &= mask.s6_addr32[3]; /* if you set extra bits, that's wrong */ if (bcmp(&match, &sin6->sin6_addr, sizeof(match))) return EINVAL; cmp = 1; } else { if (cmd == SIOCGLIFADDR) { /* on getting an address, take the 1st match */ cmp = 0; /*XXX*/ } else { /* on deleting an address, do exact match */ in6_len2mask(&mask, 128); sin6 = (struct sockaddr_in6 *)&iflr->addr; bcopy(&sin6->sin6_addr, &match, sizeof(match)); cmp = 1; } } TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; if (!cmp) break; bcopy(IFA_IN6(ifa), &candidate, sizeof(candidate)); candidate.s6_addr32[0] &= mask.s6_addr32[0]; candidate.s6_addr32[1] &= mask.s6_addr32[1]; candidate.s6_addr32[2] &= mask.s6_addr32[2]; candidate.s6_addr32[3] &= mask.s6_addr32[3]; if (IN6_ARE_ADDR_EQUAL(&candidate, &match)) break; } if (!ifa) return EADDRNOTAVAIL; ia = ifa2ia6(ifa); if (cmd == SIOCGLIFADDR) { /* fill in the if_laddrreq structure */ bcopy(&ia->ia_addr, &iflr->addr, ia->ia_addr.sin6_len); if ((ifp->if_flags & IFF_POINTOPOINT) != 0) { bcopy(&ia->ia_dstaddr, &iflr->dstaddr, ia->ia_dstaddr.sin6_len); } else bzero(&iflr->dstaddr, sizeof(iflr->dstaddr)); iflr->prefixlen = in6_mask2len(&ia->ia_prefixmask.sin6_addr); iflr->flags = ia->ia6_flags; /*XXX*/ return 0; } else { struct in6_aliasreq ifra; /* fill in6_aliasreq and do ioctl(SIOCDIFADDR_IN6) */ bzero(&ifra, sizeof(ifra)); bcopy(iflr->iflr_name, ifra.ifra_name, sizeof(ifra.ifra_name)); bcopy(&ia->ia_addr, &ifra.ifra_addr, ia->ia_addr.sin6_len); if ((ifp->if_flags & IFF_POINTOPOINT) != 0) { bcopy(&ia->ia_dstaddr, &ifra.ifra_dstaddr, ia->ia_dstaddr.sin6_len); + } else { + bzero(&ifra.ifra_dstaddr, + sizeof(ifra.ifra_dstaddr)); } bcopy(&ia->ia_prefixmask, &ifra.ifra_dstaddr, ia->ia_prefixmask.sin6_len); ifra.ifra_flags = ia->ia6_flags; return in6_control(so, SIOCDIFADDR_IN6, (caddr_t)&ifra, ifp, p); } } } return EOPNOTSUPP; /*just for safety*/ } /* * Delete any existing route for an interface. */ void in6_ifscrub(ifp, ia) register struct ifnet *ifp; register struct in6_ifaddr *ia; { if ((ia->ia_flags & IFA_ROUTE) == 0) return; if (ifp->if_flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST); else rtinit(&(ia->ia_ifa), (int)RTM_DELETE, 0); ia->ia_flags &= ~IFA_ROUTE; /* Remove ownaddr's loopback rtentry, if it exists. */ in6_ifremloop(&(ia->ia_ifa)); } /* * Initialize an interface's intetnet6 address * and routing table entry. */ int in6_ifinit(ifp, ia, sin6, scrub) struct ifnet *ifp; struct in6_ifaddr *ia; struct sockaddr_in6 *sin6; int scrub; { struct sockaddr_in6 oldaddr; int error, flags = RTF_UP; int s = splimp(); oldaddr = ia->ia_addr; ia->ia_addr = *sin6; /* * Give the interface a chance to initialize * if this is its first address, * and to validate the address if necessary. */ if (ifp->if_ioctl && (error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia))) { splx(s); ia->ia_addr = oldaddr; return(error); } switch (ifp->if_type) { case IFT_ARCNET: case IFT_ETHER: case IFT_FDDI: ia->ia_ifa.ifa_rtrequest = nd6_rtrequest; ia->ia_ifa.ifa_flags |= RTF_CLONING; break; case IFT_PPP: ia->ia_ifa.ifa_rtrequest = nd6_p2p_rtrequest; ia->ia_ifa.ifa_flags |= RTF_CLONING; break; } splx(s); if (scrub) { ia->ia_ifa.ifa_addr = (struct sockaddr *)&oldaddr; in6_ifscrub(ifp, ia); ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr; } /* xxx * in_socktrim */ /* * Add route for the network. */ ia->ia_ifa.ifa_metric = ifp->if_metric; if (ifp->if_flags & IFF_LOOPBACK) { ia->ia_ifa.ifa_dstaddr = ia->ia_ifa.ifa_addr; flags |= RTF_HOST; } else if (ifp->if_flags & IFF_POINTOPOINT) { if (ia->ia_dstaddr.sin6_family != AF_INET6) return(0); flags |= RTF_HOST; } if ((error = rtinit(&(ia->ia_ifa), (int)RTM_ADD, flags)) == 0) ia->ia_flags |= IFA_ROUTE; + /* XXX check if the subnet route points to the same interface */ + if (error == EEXIST) + error = 0; /* Add ownaddr as loopback rtentry, if necessary(ex. on p2p link). */ in6_ifaddloop(&(ia->ia_ifa)); return(error); } /* * Add an address to the list of IP6 multicast addresses for a * given interface. */ struct in6_multi * in6_addmulti(maddr6, ifp, errorp) register struct in6_addr *maddr6; register struct ifnet *ifp; int *errorp; { struct in6_multi *in6m; struct sockaddr_in6 sin6; struct ifmultiaddr *ifma; int s = splnet(); *errorp = 0; /* * Call generic routine to add membership or increment * refcount. It wants addresses in the form of a sockaddr, * so we build one here (being careful to zero the unused bytes). */ bzero(&sin6, sizeof sin6); sin6.sin6_family = AF_INET6; sin6.sin6_len = sizeof sin6; sin6.sin6_addr = *maddr6; *errorp = if_addmulti(ifp, (struct sockaddr *)&sin6, &ifma); if (*errorp) { splx(s); return 0; } /* * If ifma->ifma_protospec is null, then if_addmulti() created * a new record. Otherwise, we are done. */ if (ifma->ifma_protospec != 0) return ifma->ifma_protospec; /* XXX - if_addmulti uses M_WAITOK. Can this really be called at interrupt time? If so, need to fix if_addmulti. XXX */ in6m = (struct in6_multi *)malloc(sizeof(*in6m), M_IPMADDR, M_NOWAIT); if (in6m == NULL) { splx(s); return (NULL); } bzero(in6m, sizeof *in6m); in6m->in6m_addr = *maddr6; in6m->in6m_ifp = ifp; in6m->in6m_ifma = ifma; ifma->ifma_protospec = in6m; LIST_INSERT_HEAD(&in6_multihead, in6m, in6m_entry); /* * Let MLD6 know that we have joined a new IP6 multicast * group. */ mld6_start_listening(in6m); splx(s); return(in6m); } /* * Delete a multicast address record. */ void in6_delmulti(in6m) struct in6_multi *in6m; { struct ifmultiaddr *ifma = in6m->in6m_ifma; int s = splnet(); if (ifma->ifma_refcount == 1) { /* * No remaining claims to this record; let MLD6 know * that we are leaving the multicast group. */ mld6_stop_listening(in6m); ifma->ifma_protospec = 0; LIST_REMOVE(in6m, in6m_entry); free(in6m, M_IPMADDR); } /* XXX - should be separate API for when we have an ifma? */ if_delmulti(ifma->ifma_ifp, ifma->ifma_addr); splx(s); } /* * Find an IPv6 interface link-local address specific to an interface. */ struct in6_ifaddr * -in6ifa_ifpforlinklocal(ifp) +in6ifa_ifpforlinklocal(ifp, ignoreflags) struct ifnet *ifp; + int ignoreflags; { register struct ifaddr *ifa; TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) { if (ifa->ifa_addr == NULL) continue; /* just for safety */ if (ifa->ifa_addr->sa_family != AF_INET6) continue; - if (IN6_IS_ADDR_LINKLOCAL(IFA_IN6(ifa))) + if (IN6_IS_ADDR_LINKLOCAL(IFA_IN6(ifa))) { + if ((((struct in6_ifaddr *)ifa)->ia6_flags & + ignoreflags) != 0) + continue; break; + } } return((struct in6_ifaddr *)ifa); } /* * find the internet address corresponding to a given interface and address. */ struct in6_ifaddr * in6ifa_ifpwithaddr(ifp, addr) struct ifnet *ifp; struct in6_addr *addr; { register struct ifaddr *ifa; TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) { if (ifa->ifa_addr == NULL) continue; /* just for safety */ if (ifa->ifa_addr->sa_family != AF_INET6) continue; if (IN6_ARE_ADDR_EQUAL(addr, IFA_IN6(ifa))) break; } return((struct in6_ifaddr *)ifa); } /* * Convert IP6 address to printable (loggable) representation. */ static char digits[] = "0123456789abcdef"; static int ip6round = 0; char * ip6_sprintf(addr) register struct in6_addr *addr; { static char ip6buf[8][48]; register int i; register char *cp; register u_short *a = (u_short *)addr; register u_char *d; int dcolon = 0; ip6round = (ip6round + 1) & 7; cp = ip6buf[ip6round]; for (i = 0; i < 8; i++) { if (dcolon == 1) { if (*a == 0) { if (i == 7) *cp++ = ':'; a++; continue; } else dcolon = 2; } if (*a == 0) { if (dcolon == 0 && *(a + 1) == 0) { if (i == 0) *cp++ = ':'; *cp++ = ':'; dcolon = 1; } else { *cp++ = '0'; *cp++ = ':'; } a++; continue; } d = (u_char *)a; *cp++ = digits[*d >> 4]; *cp++ = digits[*d++ & 0xf]; *cp++ = digits[*d >> 4]; *cp++ = digits[*d & 0xf]; *cp++ = ':'; a++; } *--cp = 0; return(ip6buf[ip6round]); } int in6_localaddr(in6) struct in6_addr *in6; { struct in6_ifaddr *ia; if (IN6_IS_ADDR_LOOPBACK(in6) || IN6_IS_ADDR_LINKLOCAL(in6)) return 1; for (ia = in6_ifaddr; ia; ia = ia->ia_next) if (IN6_ARE_MASKED_ADDR_EQUAL(in6, &ia->ia_addr.sin6_addr, &ia->ia_prefixmask.sin6_addr)) return 1; return (0); } /* - * Get a scope of the address. Node-local, link-local, site-local or global. - */ -int -in6_addrscope (addr) -struct in6_addr *addr; -{ - int scope; - - if (addr->s6_addr8[0] == 0xfe) { - scope = addr->s6_addr8[1] & 0xc0; - - switch (scope) { - case 0x80: - return IPV6_ADDR_SCOPE_LINKLOCAL; - break; - case 0xc0: - return IPV6_ADDR_SCOPE_SITELOCAL; - break; - default: - return IPV6_ADDR_SCOPE_GLOBAL; /* just in case */ - break; - } - } - - - if (addr->s6_addr8[0] == 0xff) { - scope = addr->s6_addr8[1] & 0x0f; - - /* - * due to other scope such as reserved, - * return scope doesn't work. - */ - switch (scope) { - case IPV6_ADDR_SCOPE_NODELOCAL: - return IPV6_ADDR_SCOPE_NODELOCAL; - break; - case IPV6_ADDR_SCOPE_LINKLOCAL: - return IPV6_ADDR_SCOPE_LINKLOCAL; - break; - case IPV6_ADDR_SCOPE_SITELOCAL: - return IPV6_ADDR_SCOPE_SITELOCAL; - break; - default: - return IPV6_ADDR_SCOPE_GLOBAL; - break; - } - } - - if (bcmp(&in6addr_loopback, addr, sizeof(addr) - 1) == 0) { - if (addr->s6_addr8[15] == 1) /* loopback */ - return IPV6_ADDR_SCOPE_NODELOCAL; - if (addr->s6_addr8[15] == 0) /* unspecified */ - return IPV6_ADDR_SCOPE_LINKLOCAL; - } - - return IPV6_ADDR_SCOPE_GLOBAL; -} - -/* * return length of part which dst and src are equal * hard coding... */ - int in6_matchlen(src, dst) struct in6_addr *src, *dst; { int match = 0; u_char *s = (u_char *)src, *d = (u_char *)dst; u_char *lim = s + 16, r; while (s < lim) if ((r = (*d++ ^ *s++)) != 0) { while (r < 128) { match++; r <<= 1; } break; } else match += 8; return match; } +/* XXX: to be scope conscious */ int in6_are_prefix_equal(p1, p2, len) struct in6_addr *p1, *p2; int len; { int bytelen, bitlen; /* sanity check */ if (0 > len || len > 128) { log(LOG_ERR, "in6_are_prefix_equal: invalid prefix length(%d)\n", len); return(0); } bytelen = len / 8; bitlen = len % 8; if (bcmp(&p1->s6_addr, &p2->s6_addr, bytelen)) return(0); if (p1->s6_addr[bytelen] >> (8 - bitlen) != p2->s6_addr[bytelen] >> (8 - bitlen)) return(0); return(1); } void in6_prefixlen2mask(maskp, len) struct in6_addr *maskp; int len; { u_char maskarray[8] = {0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff}; int bytelen, bitlen, i; /* sanity check */ if (0 > len || len > 128) { log(LOG_ERR, "in6_prefixlen2mask: invalid prefix length(%d)\n", len); return; } bzero(maskp, sizeof(*maskp)); bytelen = len / 8; bitlen = len % 8; for (i = 0; i < bytelen; i++) maskp->s6_addr[i] = 0xff; if (bitlen) maskp->s6_addr[bytelen] = maskarray[bitlen - 1]; } /* * return the best address out of the same scope */ - struct in6_ifaddr * -in6_ifawithscope(ifp, dst) - register struct ifnet *ifp; +in6_ifawithscope(oifp, dst) + register struct ifnet *oifp; register struct in6_addr *dst; { - int dst_scope = in6_addrscope(dst), blen = -1, tlen; + int dst_scope = in6_addrscope(dst), src_scope, best_scope = 0; + int blen = -1; struct ifaddr *ifa; - struct in6_ifaddr *besta = NULL, *ia; - struct in6_ifaddr *dep[3]; /*last-resort: deprecated*/ + struct ifnet *ifp; + struct in6_ifaddr *ifa_best = NULL; + + if (oifp == NULL) { + printf("in6_ifawithscope: output interface is not specified\n"); + return(NULL); + } - dep[0] = dep[1] = dep[2] = NULL; - /* - * We first look for addresses in the same scope. - * If there is one, return it. - * If two or more, return one which matches the dst longest. - * If none, return one of global addresses assigned other ifs. + * We search for all addresses on all interfaces from the beginning. + * Comparing an interface with the outgoing interface will be done + * only at the final stage of tiebreaking. */ - TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) + for (ifp = TAILQ_FIRST(&ifnet); ifp; ifp = TAILQ_NEXT(ifp, if_list)) { - if (ifa->ifa_addr->sa_family != AF_INET6) + /* + * We can never take an address that breaks the scope zone + * of the destination. + */ + if (in6_addr2scopeid(ifp, dst) != in6_addr2scopeid(oifp, dst)) continue; - if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST) - continue; /* XXX: is there any case to allow anycast? */ - if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_NOTREADY) - continue; /* don't use this interface */ - if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DETACHED) - continue; - if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DEPRECATED) { - if (ip6_use_deprecated) - dep[0] = (struct in6_ifaddr *)ifa; - continue; - } - if (dst_scope == in6_addrscope(IFA_IN6(ifa))) { + TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) + { + int tlen = -1, dscopecmp, bscopecmp, matchcmp; + + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; + + src_scope = in6_addrscope(IFA_IN6(ifa)); + /* - * call in6_matchlen() as few as possible + * Don't use an address before completing DAD + * nor a duplicated address. */ - if (besta) { - if (blen == -1) - blen = in6_matchlen(&besta->ia_addr.sin6_addr, dst); - tlen = in6_matchlen(IFA_IN6(ifa), dst); - if (tlen > blen) { - blen = tlen; - besta = (struct in6_ifaddr *)ifa; - } - } else - besta = (struct in6_ifaddr *)ifa; - } - } - if (besta) - return besta; + if (((struct in6_ifaddr *)ifa)->ia6_flags & + IN6_IFF_NOTREADY) + continue; - for (ia = in6_ifaddr; ia; ia = ia->ia_next) { - if (IPV6_ADDR_SCOPE_GLOBAL != - in6_addrscope(&(ia->ia_addr.sin6_addr))) - continue; - /* XXX: is there any case to allow anycast? */ - if ((ia->ia6_flags & IN6_IFF_ANYCAST) != 0) - continue; - if ((ia->ia6_flags & IN6_IFF_NOTREADY) != 0) - continue; - if ((ia->ia6_flags & IN6_IFF_DETACHED) != 0) - continue; - if ((ia->ia6_flags & IN6_IFF_DEPRECATED) != 0) { - if (ip6_use_deprecated) - dep[1] = (struct in6_ifaddr *)ifa; - continue; - } - return ia; - } + /* XXX: is there any case to allow anycasts? */ + if (((struct in6_ifaddr *)ifa)->ia6_flags & + IN6_IFF_ANYCAST) + continue; - for (ia = in6_ifaddr; ia; ia = ia->ia_next) { - if (IPV6_ADDR_SCOPE_SITELOCAL != - in6_addrscope(&(ia->ia_addr.sin6_addr))) - continue; - /* XXX: is there any case to allow anycast? */ - if ((ia->ia6_flags & IN6_IFF_ANYCAST) != 0) - continue; - if ((ia->ia6_flags & IN6_IFF_NOTREADY) != 0) - continue; - if ((ia->ia6_flags & IN6_IFF_DETACHED) != 0) - continue; - if ((ia->ia6_flags & IN6_IFF_DEPRECATED) != 0) { - if (ip6_use_deprecated) - dep[2] = (struct in6_ifaddr *)ifa; - continue; + if (((struct in6_ifaddr *)ifa)->ia6_flags & + IN6_IFF_DETACHED) + continue; + + /* + * If this is the first address we find, + * keep it anyway. + */ + if (ifa_best == NULL) + goto replace; + + /* + * ifa_best is never NULL beyond this line except + * within the block labeled "replace". + */ + + /* + * If ifa_best has a smaller scope than dst and + * the current address has a larger one than + * (or equal to) dst, always replace ifa_best. + * Also, if the current address has a smaller scope + * than dst, ignore it unless ifa_best also has a + * smaller scope. + */ + if (IN6_ARE_SCOPE_CMP(best_scope, dst_scope) < 0 && + IN6_ARE_SCOPE_CMP(src_scope, dst_scope) >= 0) + goto replace; + if (IN6_ARE_SCOPE_CMP(src_scope, dst_scope) < 0 && + IN6_ARE_SCOPE_CMP(best_scope, dst_scope) >= 0) + continue; + + /* + * A deprecated address SHOULD NOT be used in new + * communications if an alternate (non-deprecated) + * address is available and has sufficient scope. + * RFC 2462, Section 5.5.4. + */ + if (((struct in6_ifaddr *)ifa)->ia6_flags & + IN6_IFF_DEPRECATED) { + /* + * Ignore any deprecated addresses if + * specified by configuration. + */ + if (!ip6_use_deprecated) + continue; + + /* + * If we have already found a non-deprecated + * candidate, just ignore deprecated addresses. + */ + if ((ifa_best->ia6_flags & IN6_IFF_DEPRECATED) + == 0) + continue; + } + + /* + * A non-deprecated address is always preferred + * to a deprecated one regardless of scopes and + * address matching. + */ + if ((ifa_best->ia6_flags & IN6_IFF_DEPRECATED) && + (((struct in6_ifaddr *)ifa)->ia6_flags & + IN6_IFF_DEPRECATED) == 0) + goto replace; + + /* + * At this point, we have two cases: + * 1. we are looking at a non-deprecated address, + * and ifa_best is also non-deprecated. + * 2. we are looking at a deprecated address, + * and ifa_best is also deprecated. + * Also, we do not have to consider a case where + * the scope of if_best is larger(smaller) than dst and + * the scope of the current address is smaller(larger) + * than dst. Such a case has already been covered. + * Tiebreaking is done according to the following + * items: + * - the scope comparison between the address and + * dst (dscopecmp) + * - the scope comparison between the address and + * ifa_best (bscopecmp) + * - if the address match dst longer than ifa_best + * (matchcmp) + * - if the address is on the outgoing I/F (outI/F) + * + * Roughly speaking, the selection policy is + * - the most important item is scope. The same scope + * is best. Then search for a larger scope. + * Smaller scopes are the last resort. + * - A deprecated address is chosen only when we have + * no address that has an enough scope, but is + * prefered to any addresses of smaller scopes. + * - Longest address match against dst is considered + * only for addresses that has the same scope of dst. + * - If there is no other reasons to choose one, + * addresses on the outgoing I/F are preferred. + * + * The precise decision table is as follows: + * dscopecmp bscopecmp matchcmp outI/F | replace? + * !equal equal N/A Yes | Yes (1) + * !equal equal N/A No | No (2) + * larger larger N/A N/A | No (3) + * larger smaller N/A N/A | Yes (4) + * smaller larger N/A N/A | Yes (5) + * smaller smaller N/A N/A | No (6) + * equal smaller N/A N/A | Yes (7) + * equal larger (already done) + * equal equal larger N/A | Yes (8) + * equal equal smaller N/A | No (9) + * equal equal equal Yes | Yes (a) + * eaual eqaul equal No | No (b) + */ + dscopecmp = IN6_ARE_SCOPE_CMP(src_scope, dst_scope); + bscopecmp = IN6_ARE_SCOPE_CMP(src_scope, best_scope); + + if (dscopecmp && bscopecmp == 0) { + if (oifp == ifp) /* (1) */ + goto replace; + continue; /* (2) */ + } + if (dscopecmp > 0) { + if (bscopecmp > 0) /* (3) */ + continue; + goto replace; /* (4) */ + } + if (dscopecmp < 0) { + if (bscopecmp > 0) /* (5) */ + goto replace; + continue; /* (6) */ + } + + /* now dscopecmp must be 0 */ + if (bscopecmp < 0) + goto replace; /* (7) */ + + /* + * At last both dscopecmp and bscopecmp must be 0. + * We need address matching against dst for + * tiebreaking. + */ + tlen = in6_matchlen(IFA_IN6(ifa), dst); + matchcmp = tlen - blen; + if (matchcmp > 0) /* (8) */ + goto replace; + if (matchcmp < 0) /* (9) */ + continue; + if (oifp == ifp) /* (a) */ + goto replace; + continue; /* (b) */ + + replace: + ifa_best = (struct in6_ifaddr *)ifa; + blen = tlen >= 0 ? tlen : + in6_matchlen(IFA_IN6(ifa), dst); + best_scope = in6_addrscope(&ifa_best->ia_addr.sin6_addr); } - return ia; } - /* use the last-resort values, that are, deprecated addresses */ - if (dep[0]) - return dep[0]; - if (dep[1]) - return dep[1]; - if (dep[2]) - return dep[2]; + /* count statistics for future improvements */ + if (ifa_best == NULL) + ip6stat.ip6s_sources_none++; + else { + if (oifp == ifa_best->ia_ifp) + ip6stat.ip6s_sources_sameif[best_scope]++; + else + ip6stat.ip6s_sources_otherif[best_scope]++; - return NULL; + if (best_scope == dst_scope) + ip6stat.ip6s_sources_samescope[best_scope]++; + else + ip6stat.ip6s_sources_otherscope[best_scope]++; + + if ((ifa_best->ia6_flags & IN6_IFF_DEPRECATED) != 0) + ip6stat.ip6s_sources_deprecated[best_scope]++; + } + + return(ifa_best); } /* * return the best address out of the same scope. if no address was * found, return the first valid address from designated IF. */ - struct in6_ifaddr * in6_ifawithifp(ifp, dst) register struct ifnet *ifp; register struct in6_addr *dst; { int dst_scope = in6_addrscope(dst), blen = -1, tlen; struct ifaddr *ifa; struct in6_ifaddr *besta = 0; struct in6_ifaddr *dep[2]; /*last-resort: deprecated*/ dep[0] = dep[1] = NULL; /* * We first look for addresses in the same scope. * If there is one, return it. * If two or more, return one which matches the dst longest. * If none, return one of global addresses assigned other ifs. */ TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST) continue; /* XXX: is there any case to allow anycast? */ if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_NOTREADY) continue; /* don't use this interface */ if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DETACHED) continue; if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DEPRECATED) { if (ip6_use_deprecated) dep[0] = (struct in6_ifaddr *)ifa; continue; } if (dst_scope == in6_addrscope(IFA_IN6(ifa))) { /* * call in6_matchlen() as few as possible */ if (besta) { if (blen == -1) blen = in6_matchlen(&besta->ia_addr.sin6_addr, dst); tlen = in6_matchlen(IFA_IN6(ifa), dst); if (tlen > blen) { blen = tlen; besta = (struct in6_ifaddr *)ifa; } } else besta = (struct in6_ifaddr *)ifa; } } if (besta) return(besta); TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST) continue; /* XXX: is there any case to allow anycast? */ if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_NOTREADY) continue; /* don't use this interface */ if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DETACHED) continue; if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DEPRECATED) { if (ip6_use_deprecated) dep[1] = (struct in6_ifaddr *)ifa; continue; } return (struct in6_ifaddr *)ifa; } /* use the last-resort values, that are, deprecated addresses */ if (dep[0]) return dep[0]; if (dep[1]) return dep[1]; return NULL; } /* * perform DAD when interface becomes IFF_UP. */ void in6_if_up(ifp) struct ifnet *ifp; { struct ifaddr *ifa; struct in6_ifaddr *ia; - struct sockaddr_dl *sdl; - int type; - struct ether_addr ea; - int off; int dad_delay; /* delay ticks before DAD output */ - bzero(&ea, sizeof(ea)); - sdl = NULL; + /* + * special cases, like 6to4, are handled in in6_ifattach + */ + in6_ifattach(ifp, NULL); - TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) - { - if (ifa->ifa_addr->sa_family == AF_INET6 - && IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr)) { - goto dad; - } - if (ifa->ifa_addr->sa_family != AF_LINK) - continue; - sdl = (struct sockaddr_dl *)ifa->ifa_addr; - break; - } - - switch (ifp->if_type) { - case IFT_SLIP: - case IFT_PPP: -#ifdef IFT_DUMMY - case IFT_DUMMY: -#endif - case IFT_GIF: - case IFT_FAITH: - type = IN6_IFT_P2P; - in6_ifattach(ifp, type, 0, 1); - break; - case IFT_ETHER: - case IFT_FDDI: - case IFT_ATM: - type = IN6_IFT_802; - if (sdl == NULL) - break; - off = sdl->sdl_nlen; - if (bcmp(&sdl->sdl_data[off], &ea, sizeof(ea)) != 0) - in6_ifattach(ifp, type, LLADDR(sdl), 0); - break; - case IFT_ARCNET: - type = IN6_IFT_ARCNET; - if (sdl == NULL) - break; - off = sdl->sdl_nlen; - if (sdl->sdl_data[off] != 0) /* XXX ?: */ - in6_ifattach(ifp, type, LLADDR(sdl), 0); - break; - default: - break; - } - -dad: dad_delay = 0; TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; ia = (struct in6_ifaddr *)ifa; if (ia->ia6_flags & IN6_IFF_TENTATIVE) nd6_dad_start(ifa, &dad_delay); } } /* * Calculate max IPv6 MTU through all the interfaces and store it * to in6_maxmtu. */ void in6_setmaxmtu() { unsigned long maxmtu = 0; struct ifnet *ifp; for (ifp = TAILQ_FIRST(&ifnet); ifp; ifp = TAILQ_NEXT(ifp, if_list)) { if ((ifp->if_flags & IFF_LOOPBACK) == 0 && nd_ifinfo[ifp->if_index].linkmtu > maxmtu) maxmtu = nd_ifinfo[ifp->if_index].linkmtu; } if (maxmtu) /* update only when maxmtu is positive */ in6_maxmtu = maxmtu; } /* * Convert sockaddr_in6 to sockaddr_in. Original sockaddr_in6 must be * v4 mapped addr or v4 compat addr */ void in6_sin6_2_sin(struct sockaddr_in *sin, struct sockaddr_in6 *sin6) { bzero(sin, sizeof(*sin)); sin->sin_len = sizeof(struct sockaddr_in); sin->sin_family = AF_INET; sin->sin_port = sin6->sin6_port; sin->sin_addr.s_addr = sin6->sin6_addr.s6_addr32[3]; } /* Convert sockaddr_in to sockaddr_in6 in v4 mapped addr format. */ void in6_sin_2_v4mapsin6(struct sockaddr_in *sin, struct sockaddr_in6 *sin6) { bzero(sin6, sizeof(*sin6)); sin6->sin6_len = sizeof(struct sockaddr_in6); sin6->sin6_family = AF_INET6; sin6->sin6_port = sin->sin_port; sin6->sin6_addr.s6_addr32[0] = 0; sin6->sin6_addr.s6_addr32[1] = 0; sin6->sin6_addr.s6_addr32[2] = IPV6_ADDR_INT32_SMP; sin6->sin6_addr.s6_addr32[3] = sin->sin_addr.s_addr; } /* Convert sockaddr_in6 into sockaddr_in. */ void in6_sin6_2_sin_in_sock(struct sockaddr *nam) { struct sockaddr_in *sin_p; struct sockaddr_in6 sin6; /* * Save original sockaddr_in6 addr and convert it * to sockaddr_in. */ sin6 = *(struct sockaddr_in6 *)nam; sin_p = (struct sockaddr_in *)nam; in6_sin6_2_sin(sin_p, &sin6); } /* Convert sockaddr_in into sockaddr_in6 in v4 mapped addr format. */ void in6_sin_2_v4mapsin6_in_sock(struct sockaddr **nam) { struct sockaddr_in *sin_p; struct sockaddr_in6 *sin6_p; MALLOC(sin6_p, struct sockaddr_in6 *, sizeof *sin6_p, M_SONAME, M_WAITOK); sin_p = (struct sockaddr_in *)*nam; in6_sin_2_v4mapsin6(sin_p, sin6_p); FREE(*nam, M_SONAME); *nam = (struct sockaddr *)sin6_p; } Index: head/sys/netinet6/in6.h =================================================================== --- head/sys/netinet6/in6.h (revision 62586) +++ head/sys/netinet6/in6.h (revision 62587) @@ -1,590 +1,593 @@ +/* $FreeBSD$ */ +/* $KAME: in6.h,v 1.48 2000/06/26 15:55:32 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Copyright (c) 1982, 1986, 1990, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)in.h 8.3 (Berkeley) 1/3/94 - * $FreeBSD$ */ -#ifndef _NETINET6_IN6_H_ -#define _NETINET6_IN6_H_ - -#if !defined(_KERNEL) && !defined(__KAME_NETINET_IN_H_INCLUDED_) +#ifndef __KAME_NETINET_IN_H_INCLUDED_ #error "do not include netinet6/in6.h directly, include netinet/in.h" #endif -#if !defined(_XOPEN_SOURCE) +#ifndef _NETINET6_IN6_H_ +#define _NETINET6_IN6_H_ + +#ifndef _XOPEN_SOURCE #include #endif /* * Identification of the network protocol stack */ -#define __KAME__ -#define __KAME_VERSION "SNAP 19991101" +#define __KAME__ +#define __KAME_VERSION "20000701/FreeBSD-current" /* * Local port number conventions: * * Ports < IPPORT_RESERVED are reserved for privileged processes (e.g. root), * unless a kernel is compiled with IPNOPRIVPORTS defined. * * When a user does a bind(2) or connect(2) with a port number of zero, * a non-conflicting local port address is chosen. * - * The default range is IPPORT_ANONMIX to IPPORT_ANONMAX, although + * The default range is IPPORT_ANONMIN to IPPORT_ANONMAX, although * that is settable by sysctl(3); net.inet.ip.anonportmin and * net.inet.ip.anonportmax respectively. * * A user may set the IPPROTO_IP option IP_PORTRANGE to change this * default assignment range. * * The value IP_PORTRANGE_DEFAULT causes the default behavior. * * The value IP_PORTRANGE_HIGH is the same as IP_PORTRANGE_DEFAULT, * and exists only for FreeBSD compatibility purposes. * * The value IP_PORTRANGE_LOW changes the range to the "low" are * that is (by convention) restricted to privileged processes. * This convention is based on "vouchsafe" principles only. * It is only secure if you trust the remote host to restrict these ports. * The range is IPPORT_RESERVEDMIN to IPPORT_RESERVEDMAX. */ #define IPV6PORT_RESERVED 1024 #define IPV6PORT_ANONMIN 49152 #define IPV6PORT_ANONMAX 65535 #define IPV6PORT_RESERVEDMIN 600 #define IPV6PORT_RESERVEDMAX (IPV6PORT_RESERVED-1) /* * IPv6 address */ struct in6_addr { union { - u_int8_t __u6_addr8[16]; - u_int16_t __u6_addr16[8]; - u_int32_t __u6_addr32[4]; + u_int8_t __u6_addr8[16]; + u_int16_t __u6_addr16[8]; + u_int32_t __u6_addr32[4]; } __u6_addr; /* 128-bit IP6 address */ }; -#define s6_addr __u6_addr.__u6_addr8 +#define s6_addr __u6_addr.__u6_addr8 #ifdef _KERNEL /*XXX nonstandard*/ -#define s6_addr8 __u6_addr.__u6_addr8 -#define s6_addr16 __u6_addr.__u6_addr16 -#define s6_addr32 __u6_addr.__u6_addr32 +#define s6_addr8 __u6_addr.__u6_addr8 +#define s6_addr16 __u6_addr.__u6_addr16 +#define s6_addr32 __u6_addr.__u6_addr32 #endif -#define INET6_ADDRSTRLEN 46 +#define INET6_ADDRSTRLEN 46 /* * Socket address for IPv6 */ -#if !defined(_XOPEN_SOURCE) -#define SIN6_LEN +#ifndef _XOPEN_SOURCE +#define SIN6_LEN #endif struct sockaddr_in6 { - u_char sin6_len; /* length of this struct(sa_family_t)*/ - u_char sin6_family; /* AF_INET6 (sa_family_t) */ + u_int8_t sin6_len; /* length of this struct(sa_family_t)*/ + u_int8_t sin6_family; /* AF_INET6 (sa_family_t) */ u_int16_t sin6_port; /* Transport layer port # (in_port_t)*/ u_int32_t sin6_flowinfo; /* IP6 flow information */ - struct in6_addr sin6_addr; /* IP6 address */ + struct in6_addr sin6_addr; /* IP6 address */ u_int32_t sin6_scope_id; /* intface scope id */ }; /* * Local definition for masks */ #ifdef _KERNEL /*XXX nonstandard*/ -#define IN6MASK0 {{{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }}} -#define IN6MASK32 {{{ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, \ +#define IN6MASK0 {{{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }}} +#define IN6MASK32 {{{ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }}} -#define IN6MASK64 {{{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ +#define IN6MASK64 {{{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }}} -#define IN6MASK96 {{{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ +#define IN6MASK96 {{{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 }}} -#define IN6MASK128 {{{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ +#define IN6MASK128 {{{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }}} #endif #ifdef _KERNEL extern const struct in6_addr in6mask0; extern const struct in6_addr in6mask32; extern const struct in6_addr in6mask64; extern const struct in6_addr in6mask96; extern const struct in6_addr in6mask128; #endif /* _KERNEL */ /* * Macros started with IPV6_ADDR is KAME local */ #ifdef _KERNEL /*XXX nonstandard*/ #if BYTE_ORDER == BIG_ENDIAN -#define IPV6_ADDR_INT32_ONE 1 -#define IPV6_ADDR_INT32_TWO 2 -#define IPV6_ADDR_INT32_MNL 0xff010000 -#define IPV6_ADDR_INT32_MLL 0xff020000 -#define IPV6_ADDR_INT32_SMP 0x0000ffff -#define IPV6_ADDR_INT16_ULL 0xfe80 -#define IPV6_ADDR_INT16_USL 0xfec0 -#define IPV6_ADDR_INT16_MLL 0xff02 +#define IPV6_ADDR_INT32_ONE 1 +#define IPV6_ADDR_INT32_TWO 2 +#define IPV6_ADDR_INT32_MNL 0xff010000 +#define IPV6_ADDR_INT32_MLL 0xff020000 +#define IPV6_ADDR_INT32_SMP 0x0000ffff +#define IPV6_ADDR_INT16_ULL 0xfe80 +#define IPV6_ADDR_INT16_USL 0xfec0 +#define IPV6_ADDR_INT16_MLL 0xff02 #elif BYTE_ORDER == LITTLE_ENDIAN -#define IPV6_ADDR_INT32_ONE 0x01000000 -#define IPV6_ADDR_INT32_TWO 0x02000000 -#define IPV6_ADDR_INT32_MNL 0x000001ff -#define IPV6_ADDR_INT32_MLL 0x000002ff -#define IPV6_ADDR_INT32_SMP 0xffff0000 -#define IPV6_ADDR_INT16_ULL 0x80fe -#define IPV6_ADDR_INT16_USL 0xc0fe -#define IPV6_ADDR_INT16_MLL 0x02ff +#define IPV6_ADDR_INT32_ONE 0x01000000 +#define IPV6_ADDR_INT32_TWO 0x02000000 +#define IPV6_ADDR_INT32_MNL 0x000001ff +#define IPV6_ADDR_INT32_MLL 0x000002ff +#define IPV6_ADDR_INT32_SMP 0xffff0000 +#define IPV6_ADDR_INT16_ULL 0x80fe +#define IPV6_ADDR_INT16_USL 0xc0fe +#define IPV6_ADDR_INT16_MLL 0x02ff #endif #endif /* * Definition of some useful macros to handle IP6 addresses */ -#define IN6ADDR_ANY_INIT \ +#define IN6ADDR_ANY_INIT \ {{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }}} -#define IN6ADDR_LOOPBACK_INIT \ +#define IN6ADDR_LOOPBACK_INIT \ {{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }}} -#define IN6ADDR_NODELOCAL_ALLNODES_INIT \ +#define IN6ADDR_NODELOCAL_ALLNODES_INIT \ {{{ 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }}} -#define IN6ADDR_LINKLOCAL_ALLNODES_INIT \ +#define IN6ADDR_LINKLOCAL_ALLNODES_INIT \ {{{ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }}} -#define IN6ADDR_LINKLOCAL_ALLROUTERS_INIT \ +#define IN6ADDR_LINKLOCAL_ALLROUTERS_INIT \ {{{ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 }}} extern const struct in6_addr in6addr_any; extern const struct in6_addr in6addr_loopback; extern const struct in6_addr in6addr_nodelocal_allnodes; extern const struct in6_addr in6addr_linklocal_allnodes; extern const struct in6_addr in6addr_linklocal_allrouters; /* * Equality * NOTE: Some of kernel programming environment (for example, openbsd/sparc) * does not supply memcmp(). For userland memcmp() is preferred as it is * in ANSI standard. */ #ifdef _KERNEL -#define IN6_ARE_ADDR_EQUAL(a, b) \ - (bcmp((a), (b), sizeof(struct in6_addr)) == 0) +#define IN6_ARE_ADDR_EQUAL(a, b) \ + (bcmp(&(a)->s6_addr[0], &(b)->s6_addr[0], sizeof(struct in6_addr)) == 0) #else -#define IN6_ARE_ADDR_EQUAL(a, b) \ - (memcmp((a), (b), sizeof(struct in6_addr)) == 0) +#define IN6_ARE_ADDR_EQUAL(a, b) \ + (memcmp(&(a)->s6_addr[0], &(b)->s6_addr[0], sizeof(struct in6_addr)) == 0) #endif /* * Unspecified */ -#define IN6_IS_ADDR_UNSPECIFIED(a) \ +#define IN6_IS_ADDR_UNSPECIFIED(a) \ ((*(u_int32_t *)(&(a)->s6_addr[0]) == 0) && \ (*(u_int32_t *)(&(a)->s6_addr[4]) == 0) && \ (*(u_int32_t *)(&(a)->s6_addr[8]) == 0) && \ (*(u_int32_t *)(&(a)->s6_addr[12]) == 0)) /* * Loopback */ -#define IN6_IS_ADDR_LOOPBACK(a) \ +#define IN6_IS_ADDR_LOOPBACK(a) \ ((*(u_int32_t *)(&(a)->s6_addr[0]) == 0) && \ (*(u_int32_t *)(&(a)->s6_addr[4]) == 0) && \ (*(u_int32_t *)(&(a)->s6_addr[8]) == 0) && \ (*(u_int32_t *)(&(a)->s6_addr[12]) == ntohl(1))) /* * IPv4 compatible */ -#define IN6_IS_ADDR_V4COMPAT(a) \ +#define IN6_IS_ADDR_V4COMPAT(a) \ ((*(u_int32_t *)(&(a)->s6_addr[0]) == 0) && \ (*(u_int32_t *)(&(a)->s6_addr[4]) == 0) && \ (*(u_int32_t *)(&(a)->s6_addr[8]) == 0) && \ (*(u_int32_t *)(&(a)->s6_addr[12]) != 0) && \ (*(u_int32_t *)(&(a)->s6_addr[12]) != ntohl(1))) /* * Mapped */ -#define IN6_IS_ADDR_V4MAPPED(a) \ +#define IN6_IS_ADDR_V4MAPPED(a) \ ((*(u_int32_t *)(&(a)->s6_addr[0]) == 0) && \ (*(u_int32_t *)(&(a)->s6_addr[4]) == 0) && \ (*(u_int32_t *)(&(a)->s6_addr[8]) == ntohl(0x0000ffff))) /* * KAME Scope Values */ #ifdef _KERNEL /*XXX nonstandard*/ -#define IPV6_ADDR_SCOPE_NODELOCAL 0x01 -#define IPV6_ADDR_SCOPE_LINKLOCAL 0x02 -#define IPV6_ADDR_SCOPE_SITELOCAL 0x05 -#define IPV6_ADDR_SCOPE_ORGLOCAL 0x08 /* just used in this file */ -#define IPV6_ADDR_SCOPE_GLOBAL 0x0e +#define IPV6_ADDR_SCOPE_NODELOCAL 0x01 +#define IPV6_ADDR_SCOPE_LINKLOCAL 0x02 +#define IPV6_ADDR_SCOPE_SITELOCAL 0x05 +#define IPV6_ADDR_SCOPE_ORGLOCAL 0x08 /* just used in this file */ +#define IPV6_ADDR_SCOPE_GLOBAL 0x0e #else -#define __IPV6_ADDR_SCOPE_NODELOCAL 0x01 -#define __IPV6_ADDR_SCOPE_LINKLOCAL 0x02 -#define __IPV6_ADDR_SCOPE_SITELOCAL 0x05 -#define __IPV6_ADDR_SCOPE_ORGLOCAL 0x08 /* just used in this file */ -#define __IPV6_ADDR_SCOPE_GLOBAL 0x0e +#define __IPV6_ADDR_SCOPE_NODELOCAL 0x01 +#define __IPV6_ADDR_SCOPE_LINKLOCAL 0x02 +#define __IPV6_ADDR_SCOPE_SITELOCAL 0x05 +#define __IPV6_ADDR_SCOPE_ORGLOCAL 0x08 /* just used in this file */ +#define __IPV6_ADDR_SCOPE_GLOBAL 0x0e #endif /* * Unicast Scope * Note that we must check topmost 10 bits only, not 16 bits (see RFC2373). */ -#define IN6_IS_ADDR_LINKLOCAL(a) \ +#define IN6_IS_ADDR_LINKLOCAL(a) \ (((a)->s6_addr[0] == 0xfe) && (((a)->s6_addr[1] & 0xc0) == 0x80)) -#define IN6_IS_ADDR_SITELOCAL(a) \ +#define IN6_IS_ADDR_SITELOCAL(a) \ (((a)->s6_addr[0] == 0xfe) && (((a)->s6_addr[1] & 0xc0) == 0xc0)) /* * Multicast */ #define IN6_IS_ADDR_MULTICAST(a) ((a)->s6_addr[0] == 0xff) #ifdef _KERNEL /*XXX nonstandard*/ -#define IPV6_ADDR_MC_SCOPE(a) ((a)->s6_addr[1] & 0x0f) +#define IPV6_ADDR_MC_SCOPE(a) ((a)->s6_addr[1] & 0x0f) #else -#define __IPV6_ADDR_MC_SCOPE(a) ((a)->s6_addr[1] & 0x0f) +#define __IPV6_ADDR_MC_SCOPE(a) ((a)->s6_addr[1] & 0x0f) #endif /* * Multicast Scope */ #ifdef _KERNEL /*refers nonstandard items */ -#define IN6_IS_ADDR_MC_NODELOCAL(a) \ +#define IN6_IS_ADDR_MC_NODELOCAL(a) \ (IN6_IS_ADDR_MULTICAST(a) && \ (IPV6_ADDR_MC_SCOPE(a) == IPV6_ADDR_SCOPE_NODELOCAL)) -#define IN6_IS_ADDR_MC_LINKLOCAL(a) \ +#define IN6_IS_ADDR_MC_LINKLOCAL(a) \ (IN6_IS_ADDR_MULTICAST(a) && \ (IPV6_ADDR_MC_SCOPE(a) == IPV6_ADDR_SCOPE_LINKLOCAL)) -#define IN6_IS_ADDR_MC_SITELOCAL(a) \ +#define IN6_IS_ADDR_MC_SITELOCAL(a) \ (IN6_IS_ADDR_MULTICAST(a) && \ (IPV6_ADDR_MC_SCOPE(a) == IPV6_ADDR_SCOPE_SITELOCAL)) -#define IN6_IS_ADDR_MC_ORGLOCAL(a) \ +#define IN6_IS_ADDR_MC_ORGLOCAL(a) \ (IN6_IS_ADDR_MULTICAST(a) && \ (IPV6_ADDR_MC_SCOPE(a) == IPV6_ADDR_SCOPE_ORGLOCAL)) -#define IN6_IS_ADDR_MC_GLOBAL(a) \ +#define IN6_IS_ADDR_MC_GLOBAL(a) \ (IN6_IS_ADDR_MULTICAST(a) && \ (IPV6_ADDR_MC_SCOPE(a) == IPV6_ADDR_SCOPE_GLOBAL)) #else -#define IN6_IS_ADDR_MC_NODELOCAL(a) \ +#define IN6_IS_ADDR_MC_NODELOCAL(a) \ (IN6_IS_ADDR_MULTICAST(a) && \ (__IPV6_ADDR_MC_SCOPE(a) == __IPV6_ADDR_SCOPE_NODELOCAL)) -#define IN6_IS_ADDR_MC_LINKLOCAL(a) \ +#define IN6_IS_ADDR_MC_LINKLOCAL(a) \ (IN6_IS_ADDR_MULTICAST(a) && \ (__IPV6_ADDR_MC_SCOPE(a) == __IPV6_ADDR_SCOPE_LINKLOCAL)) -#define IN6_IS_ADDR_MC_SITELOCAL(a) \ +#define IN6_IS_ADDR_MC_SITELOCAL(a) \ (IN6_IS_ADDR_MULTICAST(a) && \ (__IPV6_ADDR_MC_SCOPE(a) == __IPV6_ADDR_SCOPE_SITELOCAL)) -#define IN6_IS_ADDR_MC_ORGLOCAL(a) \ +#define IN6_IS_ADDR_MC_ORGLOCAL(a) \ (IN6_IS_ADDR_MULTICAST(a) && \ (__IPV6_ADDR_MC_SCOPE(a) == __IPV6_ADDR_SCOPE_ORGLOCAL)) -#define IN6_IS_ADDR_MC_GLOBAL(a) \ +#define IN6_IS_ADDR_MC_GLOBAL(a) \ (IN6_IS_ADDR_MULTICAST(a) && \ (__IPV6_ADDR_MC_SCOPE(a) == __IPV6_ADDR_SCOPE_GLOBAL)) #endif /* * KAME Scope */ #ifdef _KERNEL /*nonstandard*/ -#define IN6_IS_SCOPE_LINKLOCAL(a) \ +#define IN6_IS_SCOPE_LINKLOCAL(a) \ ((IN6_IS_ADDR_LINKLOCAL(a)) || \ (IN6_IS_ADDR_MC_LINKLOCAL(a))) #endif /* * IP6 route structure */ -#if !defined(_XOPEN_SOURCE) +#ifndef _XOPEN_SOURCE struct route_in6 { struct rtentry *ro_rt; struct sockaddr_in6 ro_dst; }; #endif /* * Options for use with [gs]etsockopt at the IPV6 level. * First word of comment is data type; bool is stored in int. */ -#define IPV6_OPTIONS 1 /* buf/ip6_opts; set/get IP6 options */ /* no hdrincl */ -#define IPV6_SOCKOPT_RESERVED1 3 /* reserved for future use */ -#define IPV6_UNICAST_HOPS 4 /* int; IP6 hops */ -#define IPV6_RECVOPTS 5 /* bool; receive all IP6 opts w/dgram */ -#define IPV6_RECVRETOPTS 6 /* bool; receive IP6 opts for response */ -#define IPV6_RECVDSTADDR 7 /* bool; receive IP6 dst addr w/dgram */ -#define IPV6_RETOPTS 8 /* ip6_opts; set/get IP6 options */ -#define IPV6_MULTICAST_IF 9 /* u_char; set/get IP6 multicast i/f */ -#define IPV6_MULTICAST_HOPS 10 /* u_char; set/get IP6 multicast hops */ -#define IPV6_MULTICAST_LOOP 11 /* u_char; set/get IP6 multicast loopback */ -#define IPV6_JOIN_GROUP 12 /* ip6_mreq; join a group membership */ -#define IPV6_LEAVE_GROUP 13 /* ip6_mreq; leave a group membership */ -#define IPV6_PORTRANGE 14 /* int; range to choose for unspec port */ -#define ICMP6_FILTER 18 /* icmp6_filter; icmp6 filter */ -#define IPV6_PKTINFO 19 /* bool; send/rcv if, src/dst addr */ -#define IPV6_HOPLIMIT 20 /* bool; hop limit */ -#define IPV6_NEXTHOP 21 /* bool; next hop addr */ -#define IPV6_HOPOPTS 22 /* bool; hop-by-hop option */ -#define IPV6_DSTOPTS 23 /* bool; destination option */ -#define IPV6_RTHDR 24 /* bool; routing header */ -#define IPV6_PKTOPTIONS 25 /* buf/cmsghdr; set/get IPv6 options */ -#define IPV6_CHECKSUM 26 /* int; checksum offset for raw socket */ -#define IPV6_BINDV6ONLY 27 /* bool; only bind INET6 at null bind */ +#if 0 /* the followings are relic in IPv4 and hence are disabled */ +#define IPV6_OPTIONS 1 /* buf/ip6_opts; set/get IP6 options */ +#define IPV6_RECVOPTS 5 /* bool; receive all IP6 opts w/dgram */ +#define IPV6_RECVRETOPTS 6 /* bool; receive IP6 opts for response */ +#define IPV6_RECVDSTADDR 7 /* bool; receive IP6 dst addr w/dgram */ +#define IPV6_RETOPTS 8 /* ip6_opts; set/get IP6 options */ +#endif +#define IPV6_SOCKOPT_RESERVED1 3 /* reserved for future use */ +#define IPV6_UNICAST_HOPS 4 /* int; IP6 hops */ +#define IPV6_MULTICAST_IF 9 /* u_char; set/get IP6 multicast i/f */ +#define IPV6_MULTICAST_HOPS 10 /* u_char; set/get IP6 multicast hops */ +#define IPV6_MULTICAST_LOOP 11 /* u_char; set/get IP6 multicast loopback */ +#define IPV6_JOIN_GROUP 12 /* ip6_mreq; join a group membership */ +#define IPV6_LEAVE_GROUP 13 /* ip6_mreq; leave a group membership */ +#define IPV6_PORTRANGE 14 /* int; range to choose for unspec port */ +#define ICMP6_FILTER 18 /* icmp6_filter; icmp6 filter */ +#define IPV6_PKTINFO 19 /* bool; send/rcv if, src/dst addr */ +#define IPV6_HOPLIMIT 20 /* bool; hop limit */ +#define IPV6_NEXTHOP 21 /* bool; next hop addr */ +#define IPV6_HOPOPTS 22 /* bool; hop-by-hop option */ +#define IPV6_DSTOPTS 23 /* bool; destination option */ +#define IPV6_RTHDR 24 /* bool; routing header */ +#define IPV6_PKTOPTIONS 25 /* buf/cmsghdr; set/get IPv6 options */ +#define IPV6_CHECKSUM 26 /* int; checksum offset for raw socket */ +#define IPV6_BINDV6ONLY 27 /* bool; only bind INET6 at null bind */ /* for IPsec */ -#define IPV6_IPSEC_POLICY 28 /* struct; get/set security policy */ -#define IPV6_FAITH 29 /* bool; accept FAITH'ed connections */ +#define IPV6_IPSEC_POLICY 28 /* struct; get/set security policy */ +#define IPV6_FAITH 29 /* bool; accept FAITH'ed connections */ /* for IPV6FIREWALL */ -#define IPV6_FW_ADD 30 /* add a firewall rule to chain */ -#define IPV6_FW_DEL 31 /* delete a firewall rule from chain */ -#define IPV6_FW_FLUSH 32 /* flush firewall rule chain */ -#define IPV6_FW_ZERO 33 /* clear single/all firewall counter(s) */ -#define IPV6_FW_GET 34 /* get entire firewall rule chain */ +#define IPV6_FW_ADD 30 /* add a firewall rule to chain */ +#define IPV6_FW_DEL 31 /* delete a firewall rule from chain */ +#define IPV6_FW_FLUSH 32 /* flush firewall rule chain */ +#define IPV6_FW_ZERO 33 /* clear single/all firewall counter(s) */ +#define IPV6_FW_GET 34 /* get entire firewall rule chain */ -#define IPV6_RTHDR_LOOSE 0 /* this hop need not be a neighbor. XXX old spec */ -#define IPV6_RTHDR_STRICT 1 /* this hop must be a neighbor. XXX old spec */ -#define IPV6_RTHDR_TYPE_0 0 /* IPv6 routing header type 0 */ +#define IPV6_RTHDR_LOOSE 0 /* this hop need not be a neighbor. XXX old spec */ +#define IPV6_RTHDR_STRICT 1 /* this hop must be a neighbor. XXX old spec */ +#define IPV6_RTHDR_TYPE_0 0 /* IPv6 routing header type 0 */ /* * Defaults and limits for options */ -#define IPV6_DEFAULT_MULTICAST_HOPS 1 /* normally limit m'casts to 1 hop */ -#define IPV6_DEFAULT_MULTICAST_LOOP 1 /* normally hear sends if a member */ +#define IPV6_DEFAULT_MULTICAST_HOPS 1 /* normally limit m'casts to 1 hop */ +#define IPV6_DEFAULT_MULTICAST_LOOP 1 /* normally hear sends if a member */ /* * Argument structure for IPV6_JOIN_GROUP and IPV6_LEAVE_GROUP. */ struct ipv6_mreq { - struct in6_addr ipv6mr_multiaddr; - u_int ipv6mr_interface; + struct in6_addr ipv6mr_multiaddr; + unsigned int ipv6mr_interface; }; /* * IPV6_PKTINFO: Packet information(RFC2292 sec 5) */ struct in6_pktinfo { - struct in6_addr ipi6_addr; /* src/dst IPv6 address */ - u_int ipi6_ifindex; /* send/recv interface index */ + struct in6_addr ipi6_addr; /* src/dst IPv6 address */ + unsigned int ipi6_ifindex; /* send/recv interface index */ }; /* * Argument for IPV6_PORTRANGE: * - which range to search when port is unspecified at bind() or connect() */ #define IPV6_PORTRANGE_DEFAULT 0 /* default range */ #define IPV6_PORTRANGE_HIGH 1 /* "high" - request firewall bypass */ #define IPV6_PORTRANGE_LOW 2 /* "low" - vouchsafe security */ -#if !defined(_XOPEN_SOURCE) +#ifndef _XOPEN_SOURCE /* * Definitions for inet6 sysctl operations. * * Third level is protocol number. * Fourth level is desired variable within that protocol. */ -#define IPV6PROTO_MAXID (IPPROTO_PIM + 1) /* don't list to IPV6PROTO_MAX */ +#define IPV6PROTO_MAXID (IPPROTO_PIM + 1) /* don't list to IPV6PROTO_MAX */ -#define CTL_IPV6PROTO_NAMES { \ +#define CTL_IPV6PROTO_NAMES { \ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \ { 0, 0 }, \ { "tcp6", CTLTYPE_NODE }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { "udp6", CTLTYPE_NODE }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \ { 0, 0 }, \ { "ip6", CTLTYPE_NODE }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \ { 0, 0 }, \ { "ipsec6", CTLTYPE_NODE }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { "icmp6", CTLTYPE_NODE }, \ { 0, 0 }, \ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { "pim6", CTLTYPE_NODE }, \ } /* * Names for IP sysctl objects */ -#define IPV6CTL_FORWARDING 1 /* act as router */ -#define IPV6CTL_SENDREDIRECTS 2 /* may send redirects when forwarding*/ -#define IPV6CTL_DEFHLIM 3 /* default Hop-Limit */ +#define IPV6CTL_FORWARDING 1 /* act as router */ +#define IPV6CTL_SENDREDIRECTS 2 /* may send redirects when forwarding*/ +#define IPV6CTL_DEFHLIM 3 /* default Hop-Limit */ #ifdef notyet -#define IPV6CTL_DEFMTU 4 /* default MTU */ +#define IPV6CTL_DEFMTU 4 /* default MTU */ #endif -#define IPV6CTL_FORWSRCRT 5 /* forward source-routed dgrams */ -#define IPV6CTL_STATS 6 /* stats */ -#define IPV6CTL_MRTSTATS 7 /* multicast forwarding stats */ -#define IPV6CTL_MRTPROTO 8 /* multicast routing protocol */ -#define IPV6CTL_MAXFRAGPACKETS 9 /* max packets reassembly queue */ -#define IPV6CTL_SOURCECHECK 10 /* verify source route and intf */ -#define IPV6CTL_SOURCECHECK_LOGINT 11 /* minimume logging interval */ -#define IPV6CTL_ACCEPT_RTADV 12 -#define IPV6CTL_KEEPFAITH 13 -#define IPV6CTL_LOG_INTERVAL 14 -#define IPV6CTL_HDRNESTLIMIT 15 -#define IPV6CTL_DAD_COUNT 16 -#define IPV6CTL_AUTO_FLOWLABEL 17 -#define IPV6CTL_DEFMCASTHLIM 18 -#define IPV6CTL_GIF_HLIM 19 /* default HLIM for gif encap packet */ -#define IPV6CTL_KAME_VERSION 20 -#define IPV6CTL_USE_DEPRECATED 21 /* use deprecated addr (RFC2462 5.5.4) */ -#define IPV6CTL_RR_PRUNE 22 /* walk timer for router renumbering */ -#define IPV6CTL_MAPPED_ADDR 23 +#define IPV6CTL_FORWSRCRT 5 /* forward source-routed dgrams */ +#define IPV6CTL_STATS 6 /* stats */ +#define IPV6CTL_MRTSTATS 7 /* multicast forwarding stats */ +#define IPV6CTL_MRTPROTO 8 /* multicast routing protocol */ +#define IPV6CTL_MAXFRAGPACKETS 9 /* max packets reassembly queue */ +#define IPV6CTL_SOURCECHECK 10 /* verify source route and intf */ +#define IPV6CTL_SOURCECHECK_LOGINT 11 /* minimume logging interval */ +#define IPV6CTL_ACCEPT_RTADV 12 +#define IPV6CTL_KEEPFAITH 13 +#define IPV6CTL_LOG_INTERVAL 14 +#define IPV6CTL_HDRNESTLIMIT 15 +#define IPV6CTL_DAD_COUNT 16 +#define IPV6CTL_AUTO_FLOWLABEL 17 +#define IPV6CTL_DEFMCASTHLIM 18 +#define IPV6CTL_GIF_HLIM 19 /* default HLIM for gif encap packet */ +#define IPV6CTL_KAME_VERSION 20 +#define IPV6CTL_USE_DEPRECATED 21 /* use deprecated addr (RFC2462 5.5.4) */ +#define IPV6CTL_RR_PRUNE 22 /* walk timer for router renumbering */ +#define IPV6CTL_MAPPED_ADDR 23 /* New entries should be added here from current IPV6CTL_MAXID value. */ -#define IPV6CTL_MAXID 24 +#define IPV6CTL_MAXID 24 #endif /* !_XOPEN_SOURCE */ /* * Redefinition of mbuf flags */ -#define M_ANYCAST6 M_PROTO1 -#define M_AUTHIPHDR M_PROTO2 -#define M_DECRYPTED M_PROTO3 -#define M_LOOP M_PROTO4 -#define M_AUTHIPDGM M_PROTO5 +#define M_ANYCAST6 M_PROTO1 +#define M_AUTHIPHDR M_PROTO2 +#define M_DECRYPTED M_PROTO3 +#define M_LOOP M_PROTO4 +#define M_AUTHIPDGM M_PROTO5 #ifdef _KERNEL struct cmsghdr; struct mbuf; struct ifnet; -int in6_canforward __P((struct in6_addr *, struct in6_addr *)); -int in6_cksum __P((struct mbuf *, u_int8_t, int, int)); +int in6_cksum __P((struct mbuf *, u_int8_t, u_int32_t, u_int32_t)); int in6_localaddr __P((struct in6_addr *)); int in6_addrscope __P((struct in6_addr *)); struct in6_ifaddr *in6_ifawithscope __P((struct ifnet *, struct in6_addr *)); struct in6_ifaddr *in6_ifawithifp __P((struct ifnet *, struct in6_addr *)); extern void in6_if_up __P((struct ifnet *)); struct sockaddr; void in6_sin6_2_sin __P((struct sockaddr_in *sin, struct sockaddr_in6 *sin6)); void in6_sin_2_v4mapsin6 __P((struct sockaddr_in *sin, struct sockaddr_in6 *sin6)); void in6_sin6_2_sin_in_sock __P((struct sockaddr *nam)); void in6_sin_2_v4mapsin6_in_sock __P((struct sockaddr **nam)); -#define satosin6(sa) ((struct sockaddr_in6 *)(sa)) -#define sin6tosa(sin6) ((struct sockaddr *)(sin6)) -#define ifatoia6(ifa) ((struct in6_ifaddr *)(ifa)) +#define satosin6(sa) ((struct sockaddr_in6 *)(sa)) +#define sin6tosa(sin6) ((struct sockaddr *)(sin6)) +#define ifatoia6(ifa) ((struct in6_ifaddr *)(ifa)) #endif /* _KERNEL */ __BEGIN_DECLS struct cmsghdr; extern int inet6_option_space __P((int)); extern int inet6_option_init __P((void *, struct cmsghdr **, int)); extern int inet6_option_append __P((struct cmsghdr *, const u_int8_t *, int, int)); extern u_int8_t *inet6_option_alloc __P((struct cmsghdr *, int, int, int)); extern int inet6_option_next __P((const struct cmsghdr *, u_int8_t **)); extern int inet6_option_find __P((const struct cmsghdr *, u_int8_t **, int)); extern size_t inet6_rthdr_space __P((int, int)); extern struct cmsghdr *inet6_rthdr_init __P((void *, int)); extern int inet6_rthdr_add __P((struct cmsghdr *, const struct in6_addr *, u_int)); extern int inet6_rthdr_lasthop __P((struct cmsghdr *, u_int)); extern int inet6_rthdr_segments __P((const struct cmsghdr *)); extern struct in6_addr *inet6_rthdr_getaddr __P((struct cmsghdr *, int)); extern int inet6_rthdr_getflags __P((const struct cmsghdr *, int)); extern int inet6_rthdr_reverse __P((const struct cmsghdr *, struct cmsghdr *)); __END_DECLS #endif /* !_NETINET6_IN6_H_ */ Index: head/sys/netinet6/in6_cksum.c =================================================================== --- head/sys/netinet6/in6_cksum.c (revision 62586) +++ head/sys/netinet6/in6_cksum.c (revision 62587) @@ -1,301 +1,321 @@ +/* $FreeBSD$ */ +/* $KAME: in6_cksum.c,v 1.6 2000/03/25 07:23:43 sumikawa Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* * Copyright (c) 1988, 1992, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)in_cksum.c 8.1 (Berkeley) 6/10/93 */ #include #include #include #include -#include +#include #include /* * Checksum routine for Internet Protocol family headers (Portable Version). * * This routine is very heavily used in the network * code and should be modified for each CPU to be as fast as possible. */ -#define ADDCARRY(x) (x > 65535 ? x -= 65535 : x) -#define REDUCE {l_util.l = sum; sum = l_util.s[0] + l_util.s[1]; ADDCARRY(sum);} +#define ADDCARRY(x) (x > 65535 ? x -= 65535 : x) +#define REDUCE {l_util.l = sum; sum = l_util.s[0] + l_util.s[1]; ADDCARRY(sum);} static union { - u_int16_t phs[4]; + u_int16_t phs[4]; struct { u_int32_t ph_len; u_int8_t ph_zero[3]; u_int8_t ph_nxt; } ph; } uph; /* * m MUST contain a continuous IP6 header. * off is a offset where TCP/UDP/ICMP6 header starts. * len is a total length of a transport segment. * (e.g. TCP header + TCP payload) */ int in6_cksum(m, nxt, off, len) register struct mbuf *m; u_int8_t nxt; - register int off, len; + u_int32_t off, len; { register u_int16_t *w; register int sum = 0; register int mlen = 0; int byte_swapped = 0; +#if 0 + int srcifid = 0, dstifid = 0; +#endif struct ip6_hdr *ip6; union { u_int8_t c[2]; u_int16_t s; } s_util; union { u_int16_t s[2]; u_int32_t l; } l_util; /* sanity check */ if (m->m_pkthdr.len < off + len) { panic("in6_cksum: mbuf len (%d) < off+len (%d+%d)\n", m->m_pkthdr.len, off, len); } /* * First create IP6 pseudo header and calculate a summary. */ ip6 = mtod(m, struct ip6_hdr *); +#if 0 + if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) { + srcifid = ip6->ip6_src.s6_addr16[1]; + ip6->ip6_src.s6_addr16[1] = 0; + } + if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) { + dstifid = ip6->ip6_dst.s6_addr16[1]; + ip6->ip6_dst.s6_addr16[1] = 0; + } +#endif w = (u_int16_t *)&ip6->ip6_src; uph.ph.ph_len = htonl(len); uph.ph.ph_nxt = nxt; /* IPv6 source address */ sum += w[0]; if (!IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) sum += w[1]; sum += w[2]; sum += w[3]; sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7]; /* IPv6 destination address */ sum += w[8]; if (!IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) sum += w[9]; sum += w[10]; sum += w[11]; sum += w[12]; sum += w[13]; sum += w[14]; sum += w[15]; /* Payload length and upper layer identifier */ sum += uph.phs[0]; sum += uph.phs[1]; sum += uph.phs[2]; sum += uph.phs[3]; +#if 0 + if (srcifid) + ip6->ip6_src.s6_addr16[1] = srcifid; + if (dstifid) + ip6->ip6_dst.s6_addr16[1] = dstifid; +#endif /* * Secondly calculate a summary of the first mbuf excluding offset. */ while (m != NULL && off > 0) { if (m->m_len <= off) off -= m->m_len; else break; m = m->m_next; } w = (u_int16_t *)(mtod(m, u_char *) + off); mlen = m->m_len - off; if (len < mlen) mlen = len; len -= mlen; /* * Force to even boundary. */ if ((1 & (long) w) && (mlen > 0)) { REDUCE; sum <<= 8; s_util.c[0] = *(u_char *)w; w = (u_int16_t *)((char *)w + 1); mlen--; byte_swapped = 1; } /* * Unroll the loop to make overhead from * branches &c small. */ while ((mlen -= 32) >= 0) { sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3]; sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7]; sum += w[8]; sum += w[9]; sum += w[10]; sum += w[11]; sum += w[12]; sum += w[13]; sum += w[14]; sum += w[15]; w += 16; } mlen += 32; while ((mlen -= 8) >= 0) { sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3]; w += 4; } mlen += 8; if (mlen == 0 && byte_swapped == 0) goto next; REDUCE; while ((mlen -= 2) >= 0) { sum += *w++; } if (byte_swapped) { REDUCE; sum <<= 8; byte_swapped = 0; if (mlen == -1) { s_util.c[1] = *(char *)w; sum += s_util.s; mlen = 0; } else mlen = -1; } else if (mlen == -1) s_util.c[0] = *(char *)w; next: m = m->m_next; /* * Lastly calculate a summary of the rest of mbufs. */ for (;m && len; m = m->m_next) { if (m->m_len == 0) continue; w = mtod(m, u_int16_t *); if (mlen == -1) { /* * The first byte of this mbuf is the continuation * of a word spanning between this mbuf and the * last mbuf. * * s_util.c[0] is already saved when scanning previous * mbuf. */ s_util.c[1] = *(char *)w; sum += s_util.s; w = (u_int16_t *)((char *)w + 1); mlen = m->m_len - 1; len--; } else mlen = m->m_len; if (len < mlen) mlen = len; len -= mlen; /* * Force to even boundary. */ if ((1 & (long) w) && (mlen > 0)) { REDUCE; sum <<= 8; s_util.c[0] = *(u_char *)w; w = (u_int16_t *)((char *)w + 1); mlen--; byte_swapped = 1; } /* * Unroll the loop to make overhead from * branches &c small. */ while ((mlen -= 32) >= 0) { sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3]; sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7]; sum += w[8]; sum += w[9]; sum += w[10]; sum += w[11]; sum += w[12]; sum += w[13]; sum += w[14]; sum += w[15]; w += 16; } mlen += 32; while ((mlen -= 8) >= 0) { sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3]; w += 4; } mlen += 8; if (mlen == 0 && byte_swapped == 0) continue; REDUCE; while ((mlen -= 2) >= 0) { sum += *w++; } if (byte_swapped) { REDUCE; sum <<= 8; byte_swapped = 0; if (mlen == -1) { s_util.c[1] = *(char *)w; sum += s_util.s; mlen = 0; } else mlen = -1; } else if (mlen == -1) s_util.c[0] = *(char *)w; } if (len) panic("in6_cksum: out of data\n"); if (mlen == -1) { /* The last mbuf has odd # of bytes. Follow the standard (the odd byte may be shifted left by 8 bits or not as determined by endian-ness of the machine) */ s_util.c[1] = 0; sum += s_util.s; } REDUCE; return (~sum & 0xffff); } Index: head/sys/netinet6/in6_gif.c =================================================================== --- head/sys/netinet6/in6_gif.c (revision 62586) +++ head/sys/netinet6/in6_gif.c (revision 62587) @@ -1,278 +1,359 @@ +/* $FreeBSD$ */ +/* $KAME: in6_gif.c,v 1.37 2000/06/17 20:34:25 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* * in6_gif.c */ #include "opt_inet.h" +#include "opt_inet6.h" #include #include #include #include #include #include -#include #include #include #include #include #ifdef INET #include #endif -#include +#include +#ifdef INET6 +#include #include #include -#include +#include +#endif #include +#ifdef INET6 #include +#endif #include #include +#ifndef offsetof +#define offsetof(s, e) ((int)&((s *)0)->e) +#endif + int in6_gif_output(ifp, family, m, rt) struct ifnet *ifp; int family; /* family of the packet to be encapsulate. */ struct mbuf *m; struct rtentry *rt; { struct gif_softc *sc = (struct gif_softc*)ifp; struct sockaddr_in6 *dst = (struct sockaddr_in6 *)&sc->gif_ro6.ro_dst; struct sockaddr_in6 *sin6_src = (struct sockaddr_in6 *)sc->gif_psrc; struct sockaddr_in6 *sin6_dst = (struct sockaddr_in6 *)sc->gif_pdst; struct ip6_hdr *ip6; int proto; u_int8_t itos, otos; if (sin6_src == NULL || sin6_dst == NULL || sin6_src->sin6_family != AF_INET6 || sin6_dst->sin6_family != AF_INET6) { m_freem(m); return EAFNOSUPPORT; } switch (family) { #ifdef INET case AF_INET: { struct ip *ip; proto = IPPROTO_IPV4; if (m->m_len < sizeof(*ip)) { m = m_pullup(m, sizeof(*ip)); if (!m) return ENOBUFS; } ip = mtod(m, struct ip *); itos = ip->ip_tos; break; } #endif +#ifdef INET6 case AF_INET6: { struct ip6_hdr *ip6; proto = IPPROTO_IPV6; if (m->m_len < sizeof(*ip6)) { m = m_pullup(m, sizeof(*ip6)); if (!m) return ENOBUFS; } ip6 = mtod(m, struct ip6_hdr *); itos = (ntohl(ip6->ip6_flow) >> 20) & 0xff; break; } +#endif default: -#ifdef DIAGNOSTIC +#ifdef DEBUG printf("in6_gif_output: warning: unknown family %d passed\n", family); #endif m_freem(m); return EAFNOSUPPORT; } - + /* prepend new IP header */ M_PREPEND(m, sizeof(struct ip6_hdr), M_DONTWAIT); if (m && m->m_len < sizeof(struct ip6_hdr)) m = m_pullup(m, sizeof(struct ip6_hdr)); if (m == NULL) { printf("ENOBUFS in in6_gif_output %d\n", __LINE__); return ENOBUFS; } ip6 = mtod(m, struct ip6_hdr *); ip6->ip6_flow = 0; - ip6->ip6_vfc = IPV6_VERSION; + ip6->ip6_vfc &= ~IPV6_VERSION_MASK; + ip6->ip6_vfc |= IPV6_VERSION; ip6->ip6_plen = htons((u_short)m->m_pkthdr.len); ip6->ip6_nxt = proto; ip6->ip6_hlim = ip6_gif_hlim; ip6->ip6_src = sin6_src->sin6_addr; if (ifp->if_flags & IFF_LINK0) { /* multi-destination mode */ if (!IN6_IS_ADDR_UNSPECIFIED(&sin6_dst->sin6_addr)) ip6->ip6_dst = sin6_dst->sin6_addr; else if (rt) { + if (family != AF_INET6) { + m_freem(m); + return EINVAL; /*XXX*/ + } ip6->ip6_dst = ((struct sockaddr_in6 *)(rt->rt_gateway))->sin6_addr; } else { m_freem(m); return ENETUNREACH; } } else { /* bidirectional configured tunnel mode */ if (!IN6_IS_ADDR_UNSPECIFIED(&sin6_dst->sin6_addr)) ip6->ip6_dst = sin6_dst->sin6_addr; else { m_freem(m); return ENETUNREACH; } } if (ifp->if_flags & IFF_LINK1) { otos = 0; ip_ecn_ingress(ECN_ALLOWED, &otos, &itos); ip6->ip6_flow |= htonl((u_int32_t)otos << 20); } if (dst->sin6_family != sin6_dst->sin6_family || !IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &sin6_dst->sin6_addr)) { /* cache route doesn't match */ bzero(dst, sizeof(*dst)); dst->sin6_family = sin6_dst->sin6_family; dst->sin6_len = sizeof(struct sockaddr_in6); dst->sin6_addr = sin6_dst->sin6_addr; if (sc->gif_ro6.ro_rt) { RTFREE(sc->gif_ro6.ro_rt); sc->gif_ro6.ro_rt = NULL; } +#if 0 + sc->gif_if.if_mtu = GIF_MTU; +#endif } if (sc->gif_ro6.ro_rt == NULL) { rtalloc((struct route *)&sc->gif_ro6); if (sc->gif_ro6.ro_rt == NULL) { m_freem(m); return ENETUNREACH; } - } + /* if it constitutes infinite encapsulation, punt. */ + if (sc->gif_ro.ro_rt->rt_ifp == ifp) { + m_freem(m); + return ENETUNREACH; /*XXX*/ + } +#if 0 + ifp->if_mtu = sc->gif_ro6.ro_rt->rt_ifp->if_mtu + - sizeof(struct ip6_hdr); +#endif + } + +#ifdef IPV6_MINMTU + /* + * force fragmentation to minimum MTU, to avoid path MTU discovery. + * it is too painful to ask for resend of inner packet, to achieve + * path MTU discovery for encapsulated packets. + */ + return(ip6_output(m, 0, &sc->gif_ro6, IPV6_MINMTU, 0, NULL)); +#else return(ip6_output(m, 0, &sc->gif_ro6, 0, 0, NULL)); +#endif } int in6_gif_input(mp, offp, proto) struct mbuf **mp; int *offp, proto; { struct mbuf *m = *mp; - struct gif_softc *sc; struct ifnet *gifp = NULL; struct ip6_hdr *ip6; - int i; int af = 0; u_int32_t otos; ip6 = mtod(m, struct ip6_hdr *); -#define satoin6(sa) (((struct sockaddr_in6 *)(sa))->sin6_addr) - for (i = 0, sc = gif; i < ngif; i++, sc++) { - if (sc->gif_psrc == NULL || - sc->gif_pdst == NULL || - sc->gif_psrc->sa_family != AF_INET6 || - sc->gif_pdst->sa_family != AF_INET6) { - continue; - } - if ((sc->gif_if.if_flags & IFF_UP) == 0) - continue; - if ((sc->gif_if.if_flags & IFF_LINK0) && - IN6_ARE_ADDR_EQUAL(&satoin6(sc->gif_psrc), &ip6->ip6_dst) && - IN6_IS_ADDR_UNSPECIFIED(&satoin6(sc->gif_pdst))) { - gifp = &sc->gif_if; - continue; - } - if (IN6_ARE_ADDR_EQUAL(&satoin6(sc->gif_psrc), &ip6->ip6_dst) && - IN6_ARE_ADDR_EQUAL(&satoin6(sc->gif_pdst), &ip6->ip6_src)) { - gifp = &sc->gif_if; - break; - } - } + gifp = (struct ifnet *)encap_getarg(m); - if (gifp == NULL) { + if (gifp == NULL || (gifp->if_flags & IFF_UP) == 0) { m_freem(m); ip6stat.ip6s_nogif++; return IPPROTO_DONE; } - + otos = ip6->ip6_flow; m_adj(m, *offp); switch (proto) { #ifdef INET case IPPROTO_IPV4: { struct ip *ip; u_int8_t otos8; af = AF_INET; otos8 = (ntohl(otos) >> 20) & 0xff; if (m->m_len < sizeof(*ip)) { m = m_pullup(m, sizeof(*ip)); if (!m) return IPPROTO_DONE; } ip = mtod(m, struct ip *); if (gifp->if_flags & IFF_LINK1) ip_ecn_egress(ECN_ALLOWED, &otos8, &ip->ip_tos); break; } #endif /* INET */ +#ifdef INET6 case IPPROTO_IPV6: { struct ip6_hdr *ip6; af = AF_INET6; if (m->m_len < sizeof(*ip6)) { m = m_pullup(m, sizeof(*ip6)); if (!m) return IPPROTO_DONE; } ip6 = mtod(m, struct ip6_hdr *); if (gifp->if_flags & IFF_LINK1) ip6_ecn_egress(ECN_ALLOWED, &otos, &ip6->ip6_flow); break; } +#endif default: ip6stat.ip6s_nogif++; m_freem(m); return IPPROTO_DONE; } - + gif_input(m, af, gifp); return IPPROTO_DONE; +} + +/* + * we know that we are in IFF_UP, outer address available, and outer family + * matched the physical addr family. see gif_encapcheck(). + */ +int +gif_encapcheck6(m, off, proto, arg) + const struct mbuf *m; + int off; + int proto; + void *arg; +{ + struct ip6_hdr ip6; + struct gif_softc *sc; + struct sockaddr_in6 *src, *dst; + int addrmatch; + + /* sanity check done in caller */ + sc = (struct gif_softc *)arg; + src = (struct sockaddr_in6 *)sc->gif_psrc; + dst = (struct sockaddr_in6 *)sc->gif_pdst; + + /* LINTED const cast */ + m_copydata((struct mbuf *)m, 0, sizeof(ip6), (caddr_t)&ip6); + + /* check for address match */ + addrmatch = 0; + if (IN6_ARE_ADDR_EQUAL(&src->sin6_addr, &ip6.ip6_dst)) + addrmatch |= 1; + if (IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &ip6.ip6_src)) + addrmatch |= 2; + else if ((sc->gif_if.if_flags & IFF_LINK0) != 0 && + IN6_IS_ADDR_UNSPECIFIED(&dst->sin6_addr)) { + addrmatch |= 2; /* we accept any source */ + } + if (addrmatch != 3) + return 0; + + /* martian filters on outer source - done in ip6_input */ + + /* ingress filters on outer source */ + if ((m->m_flags & M_PKTHDR) != 0 && m->m_pkthdr.rcvif) { + struct sockaddr_in6 sin6; + struct rtentry *rt; + + bzero(&sin6, sizeof(sin6)); + sin6.sin6_family = AF_INET6; + sin6.sin6_len = sizeof(struct sockaddr_in6); + sin6.sin6_addr = ip6.ip6_src; + /* XXX scopeid */ + rt = rtalloc1((struct sockaddr *)&sin6, 0, 0UL); + if (!rt) + return 0; + if (rt->rt_ifp != m->m_pkthdr.rcvif) { + rtfree(rt); + return 0; + } + rtfree(rt); + } + + /* prioritize: IFF_LINK0 mode is less preferred */ + return (sc->gif_if.if_flags & IFF_LINK0) ? 128 : 128 * 2; } Index: head/sys/netinet6/in6_gif.h =================================================================== --- head/sys/netinet6/in6_gif.h (revision 62586) +++ head/sys/netinet6/in6_gif.h (revision 62587) @@ -1,45 +1,42 @@ +/* $FreeBSD$ */ +/* $KAME: in6_gif.h,v 1.5 2000/04/14 08:36:03 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ #ifndef _NETINET6_IN6_GIF_H_ -#define _NETINET6_IN6_GIF_H_ +#define _NETINET6_IN6_GIF_H_ -#define GIF_HLIM 30 +#define GIF_HLIM 30 -struct mbuf; -struct ifnet; -struct rtentry; - -int in6_gif_input __P((struct mbuf **, int *, int)); -int in6_gif_output __P((struct ifnet *, int, struct mbuf *, - struct rtentry *)); +int in6_gif_input __P((struct mbuf **, int *, int)); +int in6_gif_output __P((struct ifnet *, int, struct mbuf *, struct rtentry *)); +int gif_encapcheck6 __P((const struct mbuf *, int, int, void *)); #endif /*_NETINET6_IN6_GIF_H_*/ Index: head/sys/netinet6/in6_ifattach.c =================================================================== --- head/sys/netinet6/in6_ifattach.c (revision 62586) +++ head/sys/netinet6/in6_ifattach.c (revision 62587) @@ -1,688 +1,1016 @@ +/* $FreeBSD$ */ +/* $KAME: in6_ifattach.c,v 1.61 2000/06/13 08:15:27 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include -#include +#include #include #include -#include #include #include +#include #include -static struct in6_addr llsol; +struct in6_ifstat **in6_ifstat = NULL; +struct icmp6_ifstat **icmp6_ifstat = NULL; +size_t in6_ifstatmax = 0; +size_t icmp6_ifstatmax = 0; +unsigned long in6_maxmtu = 0; -struct in6_ifstat **in6_ifstat = NULL; -struct icmp6_ifstat **icmp6_ifstat = NULL; -size_t in6_ifstatmax = 0; -size_t icmp6_ifstatmax = 0; -unsigned long in6_maxmtu = 0; +static int get_rand_ifid __P((struct ifnet *, struct in6_addr *)); +static int get_hw_ifid __P((struct ifnet *, struct in6_addr *)); +static int get_ifid __P((struct ifnet *, struct ifnet *, struct in6_addr *)); +static int in6_ifattach_addaddr __P((struct ifnet *, struct in6_ifaddr *)); +static int in6_ifattach_linklocal __P((struct ifnet *, struct ifnet *)); +static int in6_ifattach_loopback __P((struct ifnet *)); +static int nigroup __P((struct ifnet *, const char *, int, struct in6_addr *)); -int found_first_ifid = 0; -#define IFID_LEN 8 -static char first_ifid[IFID_LEN]; +#define EUI64_GBIT 0x01 +#define EUI64_UBIT 0x02 +#define EUI64_TO_IFID(in6) do {(in6)->s6_addr[8] ^= EUI64_UBIT; } while (0) +#define EUI64_GROUP(in6) ((in6)->s6_addr[8] & EUI64_GBIT) +#define EUI64_INDIVIDUAL(in6) (!EUI64_GROUP(in6)) +#define EUI64_LOCAL(in6) ((in6)->s6_addr[8] & EUI64_UBIT) +#define EUI64_UNIVERSAL(in6) (!EUI64_LOCAL(in6)) -static int laddr_to_eui64 __P((u_int8_t *, u_int8_t *, size_t)); -static int gen_rand_eui64 __P((u_int8_t *)); +#define IFID_LOCAL(in6) (!EUI64_LOCAL(in6)) +#define IFID_UNIVERSAL(in6) (!EUI64_UNIVERSAL(in6)) -static int -laddr_to_eui64(dst, src, len) - u_int8_t *dst; - u_int8_t *src; - size_t len; -{ - static u_int8_t zero[8]; - - bzero(zero, sizeof(zero)); - - switch (len) { - case 6: - if (bcmp(zero, src, 6) == 0) - return EINVAL; - dst[0] = src[0]; - dst[1] = src[1]; - dst[2] = src[2]; - dst[3] = 0xff; - dst[4] = 0xfe; - dst[5] = src[3]; - dst[6] = src[4]; - dst[7] = src[5]; - break; - case 8: - if (bcmp(zero, src, 8) == 0) - return EINVAL; - bcopy(src, dst, len); - break; - default: - return EINVAL; - } - - return 0; -} - /* * Generate a last-resort interface identifier, when the machine has no * IEEE802/EUI64 address sources. - * The address should be random, and should not change across reboot. + * The goal here is to get an interface identifier that is + * (1) random enough and (2) does not change across reboot. + * We currently use MD5(hostname) for it. */ static int -gen_rand_eui64(dst) - u_int8_t *dst; +get_rand_ifid(ifp, in6) + struct ifnet *ifp; + struct in6_addr *in6; /*upper 64bits are preserved */ { MD5_CTX ctxt; u_int8_t digest[16]; int hostnamelen = strlen(hostname); - /* generate 8bytes of pseudo-random value. */ +#if 0 + /* we need at least several letters as seed for ifid */ + if (hostnamelen < 3) + return -1; +#endif + + /* generate 8 bytes of pseudo-random value. */ bzero(&ctxt, sizeof(ctxt)); MD5Init(&ctxt); MD5Update(&ctxt, hostname, hostnamelen); MD5Final(digest, &ctxt); - /* assumes sizeof(digest) > sizeof(first_ifid) */ - bcopy(digest, dst, 8); + /* assumes sizeof(digest) > sizeof(ifid) */ + bcopy(digest, &in6->s6_addr[8], 8); /* make sure to set "u" bit to local, and "g" bit to individual. */ - dst[0] &= 0xfe; - dst[0] |= 0x02; /* EUI64 "local" */ + in6->s6_addr[8] &= ~EUI64_GBIT; /* g bit to "individual" */ + in6->s6_addr[8] |= EUI64_UBIT; /* u bit to "local" */ + /* convert EUI64 into IPv6 interface identifier */ + EUI64_TO_IFID(in6); + return 0; } /* - * Find first ifid on list of interfaces. - * This is assumed that ifp0's interface token (for example, IEEE802 MAC) - * is globally unique. We may need to have a flag parameter in the future. + * Get interface identifier for the specified interface. + * XXX assumes single sockaddr_dl (AF_LINK address) per an interface */ -int -in6_ifattach_getifid(ifp0) - struct ifnet *ifp0; -{ +static int +get_hw_ifid(ifp, in6) struct ifnet *ifp; + struct in6_addr *in6; /*upper 64bits are preserved */ +{ struct ifaddr *ifa; - u_int8_t *addr = NULL; - int addrlen = 0; struct sockaddr_dl *sdl; + u_int8_t *addr; + size_t addrlen; + static u_int8_t allzero[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + static u_int8_t allone[8] = + { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - if (found_first_ifid) - return 0; - - TAILQ_FOREACH(ifp, &ifnet, if_list) + for (ifa = ifp->if_addrlist.tqh_first; + ifa; + ifa = ifa->ifa_list.tqe_next) { - if (ifp0 != NULL && ifp0 != ifp) + if (ifa->ifa_addr->sa_family != AF_LINK) continue; - TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) - { - if (ifa->ifa_addr->sa_family != AF_LINK) - continue; - sdl = (struct sockaddr_dl *)ifa->ifa_addr; - if (sdl == NULL) - continue; - if (sdl->sdl_alen == 0) - continue; - switch (ifp->if_type) { - case IFT_ETHER: - case IFT_FDDI: - case IFT_ATM: - /* IEEE802/EUI64 cases - what others? */ - addr = LLADDR(sdl); - addrlen = sdl->sdl_alen; - /* - * to copy ifid from IEEE802/EUI64 interface, - * u bit of the source needs to be 0. - */ - if ((addr[0] & 0x02) != 0) - break; - goto found; - case IFT_ARCNET: - /* - * ARCnet interface token cannot be used as - * globally unique identifier due to its - * small bitwidth. - */ - break; - default: - break; - } - } + sdl = (struct sockaddr_dl *)ifa->ifa_addr; + if (sdl == NULL) + continue; + if (sdl->sdl_alen == 0) + continue; + + goto found; } -#ifdef DEBUG - printf("in6_ifattach_getifid: failed to get EUI64"); -#endif - return EADDRNOTAVAIL; + return -1; + found: - if (laddr_to_eui64(first_ifid, addr, addrlen) == 0) - found_first_ifid = 1; + addr = LLADDR(sdl); + addrlen = sdl->sdl_alen; - if (found_first_ifid) { - printf("%s: supplying EUI64: " - "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", - if_name(ifp), - first_ifid[0] & 0xff, first_ifid[1] & 0xff, - first_ifid[2] & 0xff, first_ifid[3] & 0xff, - first_ifid[4] & 0xff, first_ifid[5] & 0xff, - first_ifid[6] & 0xff, first_ifid[7] & 0xff); + /* get EUI64 */ + switch (ifp->if_type) { + case IFT_ETHER: + case IFT_FDDI: + case IFT_ATM: + /* IEEE802/EUI64 cases - what others? */ - /* invert u bit to convert EUI64 to RFC2373 interface ID. */ - first_ifid[0] ^= 0x02; + /* look at IEEE802/EUI64 only */ + if (addrlen != 8 && addrlen != 6) + return -1; - return 0; - } else { -#ifdef DEBUG - printf("in6_ifattach_getifid: failed to get EUI64"); + /* + * check for invalid MAC address - on bsdi, we see it a lot + * since wildboar configures all-zero MAC on pccard before + * card insertion. + */ + if (bcmp(addr, allzero, addrlen) == 0) + return -1; + if (bcmp(addr, allone, addrlen) == 0) + return -1; + + /* make EUI64 address */ + if (addrlen == 8) + bcopy(addr, &in6->s6_addr[8], 8); + else if (addrlen == 6) { + in6->s6_addr[8] = addr[0]; + in6->s6_addr[9] = addr[1]; + in6->s6_addr[10] = addr[2]; + in6->s6_addr[11] = 0xff; + in6->s6_addr[12] = 0xfe; + in6->s6_addr[13] = addr[3]; + in6->s6_addr[14] = addr[4]; + in6->s6_addr[15] = addr[5]; + } + break; + + case IFT_ARCNET: + if (addrlen != 1) + return -1; + if (!addr[0]) + return -1; + + bzero(&in6->s6_addr[8], 8); + in6->s6_addr[15] = addr[0]; + + /* + * due to insufficient bitwidth, we mark it local. + */ + in6->s6_addr[8] &= ~EUI64_GBIT; /* g bit to "individual" */ + in6->s6_addr[8] |= EUI64_UBIT; /* u bit to "local" */ + break; + + case IFT_GIF: +#ifdef IFT_STF + case IFT_STF: #endif - return EADDRNOTAVAIL; + /* + * mech-06 says: "SHOULD use IPv4 address as ifid source". + * however, IPv4 address is not very suitable as unique + * identifier source (can be renumbered). + * we don't do this. + */ + return -1; + + default: + return -1; } + + /* sanity check: g bit must not indicate "group" */ + if (EUI64_GROUP(in6)) + return -1; + + /* convert EUI64 into IPv6 interface identifier */ + EUI64_TO_IFID(in6); + + /* + * sanity check: ifid must not be all zero, avoid conflict with + * subnet router anycast + */ + if ((in6->s6_addr[8] & ~(EUI64_GBIT | EUI64_UBIT)) == 0x00 && + bcmp(&in6->s6_addr[9], allzero, 7) == 0) { + return -1; + } + + return 0; } /* - * add link-local address to *pseudo* p2p interfaces. - * get called when the first MAC address is made available in in6_ifattach(). - * - * XXX I start considering this loop as a bad idea. (itojun) + * Get interface identifier for the specified interface. If it is not + * available on ifp0, borrow interface identifier from other information + * sources. */ -void -in6_ifattach_p2p() +static int +get_ifid(ifp0, altifp, in6) + struct ifnet *ifp0; + struct ifnet *altifp; /*secondary EUI64 source*/ + struct in6_addr *in6; { struct ifnet *ifp; - /* prevent infinite loop. just in case. */ - if (found_first_ifid == 0) - return; + /* first, try to get it from the interface itself */ + if (get_hw_ifid(ifp0, in6) == 0) { +#ifdef ND6_DEBUG + printf("%s: got interface identifier from itself\n", + if_name(ifp0)); +#endif + goto success; + } - TAILQ_FOREACH(ifp, &ifnet, if_list) + /* try secondary EUI64 source. this basically is for ATM PVC */ + if (altifp && get_hw_ifid(altifp, in6) == 0) { +#ifdef ND6_DEBUG + printf("%s: got interface identifier from %s\n", + if_name(ifp0), if_name(altifp)); +#endif + goto success; + } + + /* next, try to get it from some other hardware interface */ + for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_list.tqe_next) { - switch (ifp->if_type) { - case IFT_GIF: - /* pseudo interfaces - safe to initialize here */ - in6_ifattach(ifp, IN6_IFT_P2P, 0, 0); - break; -#ifdef IFT_DUMMY - case IFT_DUMMY: + if (ifp == ifp0) + continue; + if (get_hw_ifid(ifp, in6) != 0) + continue; + + /* + * to borrow ifid from other interface, ifid needs to be + * globally unique + */ + if (IFID_UNIVERSAL(in6)) { + +#ifdef ND6_DEBUG + printf("%s: borrow interface identifier from %s\n", + if_name(ifp0), if_name(ifp)); #endif - case IFT_FAITH: - /* this mistakingly becomes IFF_UP */ + goto success; + } + } + + /* last resort: get from random number source */ + if (get_rand_ifid(ifp, in6) == 0) { +#ifdef ND6_DEBUG + printf("%s: interface identifier generated by random number\n", + if_name(ifp0)); +#endif + goto success; + } + + printf("%s: failed to get interface identifier", if_name(ifp0)); + return -1; + +success: +#ifdef ND6_DEBUG + printf("%s: ifid: " + "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", + if_name(ifp0), + in6->s6_addr[8], in6->s6_addr[9], + in6->s6_addr[10], in6->s6_addr[11], + in6->s6_addr[12], in6->s6_addr[13], + in6->s6_addr[14], in6->s6_addr[15]); +#endif + return 0; +} + +/* + * configure IPv6 interface address. XXX code duplicated with in.c + */ +static int +in6_ifattach_addaddr(ifp, ia) + struct ifnet *ifp; + struct in6_ifaddr *ia; +{ + struct in6_ifaddr *oia; + struct ifaddr *ifa; + int error; + int rtflag; + struct in6_addr llsol; + + /* + * initialize if_addrlist, if we are the very first one + */ + ifa = TAILQ_FIRST(&ifp->if_addrlist); + if (ifa == NULL) { + TAILQ_INIT(&ifp->if_addrlist); + } + + /* + * link the interface address to global list + */ + TAILQ_INSERT_TAIL(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list); + /* gain a refcnt for the link from if_addrlist */ + ia->ia_ifa.ifa_refcnt++; + + /* + * Also link into the IPv6 address chain beginning with in6_ifaddr. + * kazu opposed it, but itojun & jinmei wanted. + */ + if ((oia = in6_ifaddr) != NULL) { + for (; oia->ia_next; oia = oia->ia_next) + continue; + oia->ia_next = ia; + } else + in6_ifaddr = ia; + /* gain another refcnt for the link from in6_ifaddr */ + ia->ia_ifa.ifa_refcnt++; + + /* + * give the interface a chance to initialize, in case this + * is the first address to be added. + */ + if (ifp->if_ioctl != NULL) { + int s; + s = splimp(); + error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia); + splx(s); + } else + error = 0; + if (error) { + switch (error) { + case EAFNOSUPPORT: + printf("%s: IPv6 not supported\n", if_name(ifp)); break; - case IFT_SLIP: - /* IPv6 is not supported */ + default: + printf("%s: SIOCSIFADDR error %d\n", if_name(ifp), + error); break; - case IFT_PPP: - /* this is not a pseudo interface, skip it */ + } + + /* undo changes */ + TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list); + IFAFREE(&ia->ia_ifa); + if (oia) + oia->ia_next = ia->ia_next; + else + in6_ifaddr = ia->ia_next; + IFAFREE(&ia->ia_ifa); + return -1; + } + + /* configure link-layer address resolution */ + rtflag = 0; + if (IN6_ARE_ADDR_EQUAL(&ia->ia_prefixmask.sin6_addr, &in6mask128)) + rtflag = RTF_HOST; + else { + switch (ifp->if_type) { + case IFT_LOOP: +#ifdef IFT_STF + case IFT_STF: +#endif + rtflag = 0; break; default: + ia->ia_ifa.ifa_rtrequest = nd6_rtrequest; + ia->ia_ifa.ifa_flags |= RTF_CLONING; + rtflag = RTF_CLONING; break; } } + + /* add route to the interface. */ + { + int e; + + e = rtrequest(RTM_ADD, + (struct sockaddr *)&ia->ia_addr, + (struct sockaddr *)&ia->ia_addr, + (struct sockaddr *)&ia->ia_prefixmask, + RTF_UP | rtflag, + (struct rtentry **)0); + if (e) { + printf("in6_ifattach_addaddr: rtrequest failed. errno = %d\n", e); + } + } + ia->ia_flags |= IFA_ROUTE; + + if ((rtflag & RTF_CLONING) != 0 && + (ifp->if_flags & IFF_MULTICAST) != 0) { + /* + * join solicited multicast address + */ + bzero(&llsol, sizeof(llsol)); + llsol.s6_addr16[0] = htons(0xff02); + llsol.s6_addr16[1] = htons(ifp->if_index); + llsol.s6_addr32[1] = 0; + llsol.s6_addr32[2] = htonl(1); + llsol.s6_addr32[3] = ia->ia_addr.sin6_addr.s6_addr32[3]; + llsol.s6_addr8[12] = 0xff; + (void)in6_addmulti(&llsol, ifp, &error); + + /* XXX should we run DAD on other interface types? */ + switch (ifp->if_type) { +#if 1 + case IFT_ARCNET: + case IFT_ETHER: + case IFT_FDDI: +#else + default: +#endif + /* mark the address TENTATIVE, if needed. */ + ia->ia6_flags |= IN6_IFF_TENTATIVE; + /* nd6_dad_start() will be called in in6_if_up */ + } + } + + return 0; } -void -in6_ifattach(ifp, type, laddr, noloop) +static int +in6_ifattach_linklocal(ifp, altifp) struct ifnet *ifp; - u_int type; - caddr_t laddr; - /* size_t laddrlen; */ - int noloop; + struct ifnet *altifp; /*secondary EUI64 source*/ { - static size_t if_indexlim = 8; - struct sockaddr_in6 mltaddr; - struct sockaddr_in6 mltmask; - struct sockaddr_in6 gate; - struct sockaddr_in6 mask; + struct in6_ifaddr *ia; - struct in6_ifaddr *ia, *ib, *oia; - struct ifaddr *ifa; - int rtflag = 0; + /* + * configure link-local address + */ + ia = (struct in6_ifaddr *)malloc(sizeof(*ia), M_IFADDR, M_WAITOK); + bzero((caddr_t)ia, sizeof(*ia)); + ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr; + if (ifp->if_flags & IFF_POINTOPOINT) + ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr; + else + ia->ia_ifa.ifa_dstaddr = NULL; + ia->ia_ifa.ifa_netmask = (struct sockaddr *)&ia->ia_prefixmask; + ia->ia_ifp = ifp; - if (type == IN6_IFT_P2P && found_first_ifid == 0) { - printf("%s: no ifid available for IPv6 link-local address\n", - if_name(ifp)); - /* last resort */ - if (gen_rand_eui64(first_ifid) == 0) { - printf("%s: using random value as EUI64: " - "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", - if_name(ifp), - first_ifid[0] & 0xff, first_ifid[1] & 0xff, - first_ifid[2] & 0xff, first_ifid[3] & 0xff, - first_ifid[4] & 0xff, first_ifid[5] & 0xff, - first_ifid[6] & 0xff, first_ifid[7] & 0xff); - /* - * invert u bit to convert EUI64 to RFC2373 interface - * ID. - */ - first_ifid[0] ^= 0x02; + bzero(&ia->ia_prefixmask, sizeof(ia->ia_prefixmask)); + ia->ia_prefixmask.sin6_len = sizeof(struct sockaddr_in6); + ia->ia_prefixmask.sin6_family = AF_INET6; +#ifdef SCOPEDROUTING + /* take into accound the sin6_scope_id field for routing */ + ia->ia_prefixmask.sin6_scope_id = 0xffffffff; +#endif + ia->ia_prefixmask.sin6_addr = in6mask64; - found_first_ifid = 1; + /* just in case */ + bzero(&ia->ia_dstaddr, sizeof(ia->ia_dstaddr)); + ia->ia_dstaddr.sin6_len = sizeof(struct sockaddr_in6); + ia->ia_dstaddr.sin6_family = AF_INET6; + + bzero(&ia->ia_addr, sizeof(ia->ia_addr)); + ia->ia_addr.sin6_len = sizeof(struct sockaddr_in6); + ia->ia_addr.sin6_family = AF_INET6; + ia->ia_addr.sin6_addr.s6_addr16[0] = htons(0xfe80); + ia->ia_addr.sin6_addr.s6_addr16[1] = htons(ifp->if_index); + ia->ia_addr.sin6_addr.s6_addr32[1] = 0; + if (ifp->if_flags & IFF_LOOPBACK) { + ia->ia_addr.sin6_addr.s6_addr32[2] = 0; + ia->ia_addr.sin6_addr.s6_addr32[3] = htonl(1); + } else { + if (get_ifid(ifp, altifp, &ia->ia_addr.sin6_addr) != 0) { +#ifdef ND6_DEBUG + printf("%s: no ifid available\n", if_name(ifp)); +#endif + free(ia, M_IFADDR); + return -1; } } +#ifdef SCOPEDROUTING + ia->ia_addr.sin6_scope_id = in6_addr2scopeid(ifp, + &ia->ia_addr.sin6_addr); +#endif - if ((ifp->if_flags & IFF_MULTICAST) == 0) { - printf("%s: not multicast capable, IPv6 not enabled\n", - if_name(ifp)); + ia->ia_ifa.ifa_metric = ifp->if_metric; + + if (in6_ifattach_addaddr(ifp, ia) != 0) { + /* ia will be freed on failure */ + return -1; + } + + return 0; +} + +static int +in6_ifattach_loopback(ifp) + struct ifnet *ifp; /* must be IFT_LOOP */ +{ + struct in6_ifaddr *ia; + + /* + * configure link-local address + */ + ia = (struct in6_ifaddr *)malloc(sizeof(*ia), M_IFADDR, M_WAITOK); + bzero((caddr_t)ia, sizeof(*ia)); + ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr; + ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr; + ia->ia_ifa.ifa_netmask = (struct sockaddr *)&ia->ia_prefixmask; + ia->ia_ifp = ifp; + + bzero(&ia->ia_prefixmask, sizeof(ia->ia_prefixmask)); + ia->ia_prefixmask.sin6_len = sizeof(struct sockaddr_in6); + ia->ia_prefixmask.sin6_family = AF_INET6; + ia->ia_prefixmask.sin6_addr = in6mask128; + + /* + * Always initialize ia_dstaddr (= broadcast address) to loopback + * address, to make getifaddr happier. + * + * For BSDI, it is mandatory. The BSDI version of + * ifa_ifwithroute() rejects to add a route to the loopback + * interface. Even for other systems, loopback looks somewhat + * special. + */ + bzero(&ia->ia_dstaddr, sizeof(ia->ia_dstaddr)); + ia->ia_dstaddr.sin6_len = sizeof(struct sockaddr_in6); + ia->ia_dstaddr.sin6_family = AF_INET6; + ia->ia_dstaddr.sin6_addr = in6addr_loopback; + + bzero(&ia->ia_addr, sizeof(ia->ia_addr)); + ia->ia_addr.sin6_len = sizeof(struct sockaddr_in6); + ia->ia_addr.sin6_family = AF_INET6; + ia->ia_addr.sin6_addr = in6addr_loopback; + + ia->ia_ifa.ifa_metric = ifp->if_metric; + + if (in6_ifattach_addaddr(ifp, ia) != 0) { + /* ia will be freed on failure */ + return -1; + } + + return 0; +} + +/* + * compute NI group address, based on the current hostname setting. + * see draft-ietf-ipngwg-icmp-name-lookup-* (04 and later). + * + * when ifp == NULL, the caller is responsible for filling scopeid. + */ +static int +nigroup(ifp, name, namelen, in6) + struct ifnet *ifp; + const char *name; + int namelen; + struct in6_addr *in6; +{ + const char *p; + MD5_CTX ctxt; + u_int8_t digest[16]; + char l; + + if (!namelen || !name) + return -1; + + p = name; + while (p && *p && *p != '.' && p - name < namelen) + p++; + if (p - name > 63) + return -1; /*label too long*/ + l = p - name; + + /* generate 8 bytes of pseudo-random value. */ + bzero(&ctxt, sizeof(ctxt)); + MD5Init(&ctxt); + MD5Update(&ctxt, &l, sizeof(l)); + /* LINTED const cast */ + MD5Update(&ctxt, (void *)name, p - name); + MD5Final(digest, &ctxt); + + bzero(in6, sizeof(*in6)); + in6->s6_addr16[0] = htons(0xff02); + if (ifp) + in6->s6_addr16[1] = htons(ifp->if_index); + in6->s6_addr8[11] = 2; + bcopy(digest, &in6->s6_addr32[3], sizeof(in6->s6_addr32[3])); + + return 0; +} + +void +in6_nigroup_attach(name, namelen) + const char *name; + int namelen; +{ + struct ifnet *ifp; + struct sockaddr_in6 mltaddr; + struct in6_multi *in6m; + int error; + + bzero(&mltaddr, sizeof(mltaddr)); + mltaddr.sin6_family = AF_INET6; + mltaddr.sin6_len = sizeof(struct sockaddr_in6); + if (nigroup(NULL, name, namelen, &mltaddr.sin6_addr) != 0) return; + + for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_list.tqe_next) + { + mltaddr.sin6_addr.s6_addr16[1] = htons(ifp->if_index); + IN6_LOOKUP_MULTI(mltaddr.sin6_addr, ifp, in6m); + if (!in6m) + (void)in6_addmulti(&mltaddr.sin6_addr, ifp, &error); } +} +void +in6_nigroup_detach(name, namelen) + const char *name; + int namelen; +{ + struct ifnet *ifp; + struct sockaddr_in6 mltaddr; + struct in6_multi *in6m; + + bzero(&mltaddr, sizeof(mltaddr)); + mltaddr.sin6_family = AF_INET6; + mltaddr.sin6_len = sizeof(struct sockaddr_in6); + if (nigroup(NULL, name, namelen, &mltaddr.sin6_addr) != 0) + return; + + for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_list.tqe_next) + { + mltaddr.sin6_addr.s6_addr16[1] = htons(ifp->if_index); + IN6_LOOKUP_MULTI(mltaddr.sin6_addr, ifp, in6m); + if (in6m) + in6_delmulti(in6m); + } +} + +/* + * XXX multiple loopback interface needs more care. for instance, + * nodelocal address needs to be configured onto only one of them. + * XXX multiple link-local address case + */ +void +in6_ifattach(ifp, altifp) + struct ifnet *ifp; + struct ifnet *altifp; /* secondary EUI64 source */ +{ + static size_t if_indexlim = 8; + struct sockaddr_in6 mltaddr; + struct sockaddr_in6 mltmask; + struct sockaddr_in6 gate; + struct sockaddr_in6 mask; + struct in6_ifaddr *ia; + struct in6_addr in6; + int hostnamelen = strlen(hostname); + /* * We have some arrays that should be indexed by if_index. * since if_index will grow dynamically, they should grow too. * struct in6_ifstat **in6_ifstat * struct icmp6_ifstat **icmp6_ifstat */ - if (in6_ifstat == NULL || icmp6_ifstat == NULL - || if_index >= if_indexlim) { + if (in6_ifstat == NULL || icmp6_ifstat == NULL || + if_index >= if_indexlim) { size_t n; caddr_t q; size_t olim; olim = if_indexlim; while (if_index >= if_indexlim) if_indexlim <<= 1; /* grow in6_ifstat */ n = if_indexlim * sizeof(struct in6_ifstat *); q = (caddr_t)malloc(n, M_IFADDR, M_WAITOK); bzero(q, n); if (in6_ifstat) { bcopy((caddr_t)in6_ifstat, q, olim * sizeof(struct in6_ifstat *)); free((caddr_t)in6_ifstat, M_IFADDR); } in6_ifstat = (struct in6_ifstat **)q; in6_ifstatmax = if_indexlim; /* grow icmp6_ifstat */ n = if_indexlim * sizeof(struct icmp6_ifstat *); q = (caddr_t)malloc(n, M_IFADDR, M_WAITOK); bzero(q, n); if (icmp6_ifstat) { bcopy((caddr_t)icmp6_ifstat, q, olim * sizeof(struct icmp6_ifstat *)); free((caddr_t)icmp6_ifstat, M_IFADDR); } icmp6_ifstat = (struct icmp6_ifstat **)q; icmp6_ifstatmax = if_indexlim; } + /* initialize scope identifiers */ + scope6_ifattach(ifp); + /* - * To prevent to assign link-local address to PnP network - * cards multiple times. - * This is lengthy for P2P and LOOP but works. + * quirks based on interface type */ - ifa = TAILQ_FIRST(&ifp->if_addrlist); - if (ifa != NULL) { - for ( ; ifa; ifa = TAILQ_NEXT(ifa, ifa_list)) { - if (ifa->ifa_addr->sa_family != AF_INET6) - continue; - if (IN6_IS_ADDR_LINKLOCAL(&satosin6(ifa->ifa_addr)->sin6_addr)) - return; - } - } else { - TAILQ_INIT(&ifp->if_addrlist); + switch (ifp->if_type) { +#ifdef IFT_STF + case IFT_STF: + /* + * 6to4 interface is a very speical kind of beast. + * no multicast, no linklocal (based on 03 draft). + */ + goto statinit; +#endif + default: + break; } /* - * link-local address + * usually, we require multicast capability to the interface */ - ia = (struct in6_ifaddr *)malloc(sizeof(*ia), M_IFADDR, M_WAITOK); - bzero((caddr_t)ia, sizeof(*ia)); - ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr; - ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr; - ia->ia_ifa.ifa_netmask = (struct sockaddr *)&ia->ia_prefixmask; - ia->ia_ifp = ifp; - TAILQ_INSERT_TAIL(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list); + if ((ifp->if_flags & IFF_MULTICAST) == 0) { + printf("%s: not multicast capable, IPv6 not enabled\n", + if_name(ifp)); + return; + } + /* - * Also link into the IPv6 address chain beginning with in6_ifaddr. - * kazu opposed it, but itojun & jinmei wanted. + * assign link-local address, if there's none */ - if ((oia = in6_ifaddr) != NULL) { - for (; oia->ia_next; oia = oia->ia_next) - continue; - oia->ia_next = ia; - } else - in6_ifaddr = ia; + ia = in6ifa_ifpforlinklocal(ifp, 0); + if (ia == NULL) { + if (in6_ifattach_linklocal(ifp, altifp) != 0) + return; + ia = in6ifa_ifpforlinklocal(ifp, 0); - ia->ia_prefixmask.sin6_len = sizeof(struct sockaddr_in6); - ia->ia_prefixmask.sin6_family = AF_INET6; - ia->ia_prefixmask.sin6_addr = in6mask64; + if (ia == NULL) { + printf("%s: failed to add link-local address", + if_name(ifp)); - bzero(&ia->ia_addr, sizeof(struct sockaddr_in6)); - ia->ia_addr.sin6_len = sizeof(struct sockaddr_in6); - ia->ia_addr.sin6_family = AF_INET6; - ia->ia_addr.sin6_addr.s6_addr16[0] = htons(0xfe80); - ia->ia_addr.sin6_addr.s6_addr16[1] = htons(ifp->if_index); - ia->ia_addr.sin6_addr.s6_addr32[1] = 0; - - switch (type) { - case IN6_IFT_LOOP: - ia->ia_addr.sin6_addr.s6_addr32[2] = 0; - ia->ia_addr.sin6_addr.s6_addr32[3] = htonl(1); - break; - case IN6_IFT_802: - ia->ia_ifa.ifa_rtrequest = nd6_rtrequest; - ia->ia_ifa.ifa_flags |= RTF_CLONING; - rtflag = RTF_CLONING; - /* fall through */ - case IN6_IFT_P2P802: - if (laddr == NULL) - break; - /* XXX use laddrlen */ - if (laddr_to_eui64(&ia->ia_addr.sin6_addr.s6_addr8[8], - laddr, 6) != 0) { - break; + /* we can't initialize multicasts without link-local */ + goto statinit; } - /* invert u bit to convert EUI64 to RFC2373 interface ID. */ - ia->ia_addr.sin6_addr.s6_addr8[8] ^= 0x02; - if (found_first_ifid == 0) { - if (in6_ifattach_getifid(ifp) == 0) - in6_ifattach_p2p(); - } - break; - case IN6_IFT_P2P: - bcopy((caddr_t)first_ifid, - (caddr_t)&ia->ia_addr.sin6_addr.s6_addr8[8], - IFID_LEN); - break; - case IN6_IFT_ARCNET: - ia->ia_ifa.ifa_rtrequest = nd6_rtrequest; - ia->ia_ifa.ifa_flags |= RTF_CLONING; - rtflag = RTF_CLONING; - if (laddr == NULL) - break; - - /* make non-global IF id out of link-level address */ - bzero(&ia->ia_addr.sin6_addr.s6_addr8[8], 7); - ia->ia_addr.sin6_addr.s6_addr8[15] = *laddr; } - ia->ia_ifa.ifa_metric = ifp->if_metric; - - if (ifp->if_ioctl != NULL) { - int s; - int error; - + if (ifp->if_flags & IFF_POINTOPOINT) { /* - * give the interface a chance to initialize, in case this - * is the first address to be added. - */ - s = splimp(); - error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia); - splx(s); - - if (error) { - switch (error) { - case EAFNOSUPPORT: - printf("%s: IPv6 not supported\n", - if_name(ifp)); - break; - default: - printf("%s: SIOCSIFADDR error %d\n", - if_name(ifp), error); - break; - } - - /* undo changes */ - TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list); - if (oia) - oia->ia_next = ia->ia_next; - else - in6_ifaddr = ia->ia_next; - free(ia, M_IFADDR); - return; - } - } - - /* add route to the interface. */ - rtrequest(RTM_ADD, - (struct sockaddr *)&ia->ia_addr, - (struct sockaddr *)&ia->ia_addr, - (struct sockaddr *)&ia->ia_prefixmask, - RTF_UP|rtflag, - (struct rtentry **)0); - ia->ia_flags |= IFA_ROUTE; - - if (type == IN6_IFT_P2P || type == IN6_IFT_P2P802) { - /* * route local address to loopback */ bzero(&gate, sizeof(gate)); gate.sin6_len = sizeof(struct sockaddr_in6); gate.sin6_family = AF_INET6; gate.sin6_addr = in6addr_loopback; bzero(&mask, sizeof(mask)); mask.sin6_len = sizeof(struct sockaddr_in6); mask.sin6_family = AF_INET6; mask.sin6_addr = in6mask64; rtrequest(RTM_ADD, (struct sockaddr *)&ia->ia_addr, (struct sockaddr *)&gate, (struct sockaddr *)&mask, RTF_UP|RTF_HOST, (struct rtentry **)0); } /* - * loopback address + * assign loopback address for loopback interface + * XXX multiple loopback interface case */ - ib = (struct in6_ifaddr *)NULL; - if (type == IN6_IFT_LOOP) { - ib = (struct in6_ifaddr *) - malloc(sizeof(*ib), M_IFADDR, M_WAITOK); - bzero((caddr_t)ib, sizeof(*ib)); - ib->ia_ifa.ifa_addr = (struct sockaddr *)&ib->ia_addr; - ib->ia_ifa.ifa_dstaddr = (struct sockaddr *)&ib->ia_dstaddr; - ib->ia_ifa.ifa_netmask = (struct sockaddr *)&ib->ia_prefixmask; - ib->ia_ifp = ifp; + in6 = in6addr_loopback; + if (ifp->if_flags & IFF_LOOPBACK) { + if (in6ifa_ifpwithaddr(ifp, &in6) == NULL) { + if (in6_ifattach_loopback(ifp) != 0) + return; + } + } - ia->ia_next = ib; - TAILQ_INSERT_TAIL(&ifp->if_addrlist, (struct ifaddr *)ib, - ifa_list); - - ib->ia_prefixmask.sin6_len = sizeof(struct sockaddr_in6); - ib->ia_prefixmask.sin6_family = AF_INET6; - ib->ia_prefixmask.sin6_addr = in6mask128; - ib->ia_addr.sin6_len = sizeof(struct sockaddr_in6); - ib->ia_addr.sin6_family = AF_INET6; - ib->ia_addr.sin6_addr = in6addr_loopback; - ib->ia_ifa.ifa_metric = ifp->if_metric; - - rtrequest(RTM_ADD, - (struct sockaddr *)&ib->ia_addr, - (struct sockaddr *)&ib->ia_addr, - (struct sockaddr *)&ib->ia_prefixmask, - RTF_UP|RTF_HOST, - (struct rtentry **)0); - - ib->ia_flags |= IFA_ROUTE; +#ifdef DIAGNOSTIC + if (!ia) { + panic("ia == NULL in in6_ifattach"); + /*NOTREACHED*/ } +#endif /* * join multicast */ if (ifp->if_flags & IFF_MULTICAST) { int error; /* not used */ + struct in6_multi *in6m; bzero(&mltmask, sizeof(mltmask)); mltmask.sin6_len = sizeof(struct sockaddr_in6); mltmask.sin6_family = AF_INET6; mltmask.sin6_addr = in6mask32; /* * join link-local all-nodes address */ bzero(&mltaddr, sizeof(mltaddr)); mltaddr.sin6_len = sizeof(struct sockaddr_in6); mltaddr.sin6_family = AF_INET6; mltaddr.sin6_addr = in6addr_linklocal_allnodes; mltaddr.sin6_addr.s6_addr16[1] = htons(ifp->if_index); - rtrequest(RTM_ADD, - (struct sockaddr *)&mltaddr, - (struct sockaddr *)&ia->ia_addr, - (struct sockaddr *)&mltmask, - RTF_UP|RTF_CLONING, /* xxx */ - (struct rtentry **)0); - (void)in6_addmulti(&mltaddr.sin6_addr, ifp, &error); - if (type == IN6_IFT_LOOP) { - /* - * join node-local all-nodes address - */ - mltaddr.sin6_addr = in6addr_nodelocal_allnodes; + IN6_LOOKUP_MULTI(mltaddr.sin6_addr, ifp, in6m); + if (in6m == NULL) { rtrequest(RTM_ADD, (struct sockaddr *)&mltaddr, - (struct sockaddr *)&ib->ia_addr, + (struct sockaddr *)&ia->ia_addr, (struct sockaddr *)&mltmask, - RTF_UP, + RTF_UP|RTF_CLONING, /* xxx */ (struct rtentry **)0); (void)in6_addmulti(&mltaddr.sin6_addr, ifp, &error); - } else { + } + + /* + * join node information group address + */ + if (nigroup(ifp, hostname, hostnamelen, &mltaddr.sin6_addr) + == 0) { + IN6_LOOKUP_MULTI(mltaddr.sin6_addr, ifp, in6m); + if (in6m == NULL && ia != NULL) { + (void)in6_addmulti(&mltaddr.sin6_addr, + ifp, &error); + } + } + + if (ifp->if_flags & IFF_LOOPBACK) { + in6 = in6addr_loopback; + ia = in6ifa_ifpwithaddr(ifp, &in6); /* - * join solicited multicast address + * join node-local all-nodes address, on loopback */ - bzero(&llsol, sizeof(llsol)); - llsol.s6_addr16[0] = htons(0xff02); - llsol.s6_addr16[1] = htons(ifp->if_index); - llsol.s6_addr32[1] = 0; - llsol.s6_addr32[2] = htonl(1); - llsol.s6_addr32[3] = ia->ia_addr.sin6_addr.s6_addr32[3]; - llsol.s6_addr8[12] = 0xff; - (void)in6_addmulti(&llsol, ifp, &error); + mltaddr.sin6_addr = in6addr_nodelocal_allnodes; + + IN6_LOOKUP_MULTI(mltaddr.sin6_addr, ifp, in6m); + if (in6m == NULL && ia != NULL) { + rtrequest(RTM_ADD, + (struct sockaddr *)&mltaddr, + (struct sockaddr *)&ia->ia_addr, + (struct sockaddr *)&mltmask, + RTF_UP, + (struct rtentry **)0); + (void)in6_addmulti(&mltaddr.sin6_addr, ifp, &error); + } } } +statinit:; + /* update dynamically. */ if (in6_maxmtu < ifp->if_mtu) in6_maxmtu = ifp->if_mtu; if (in6_ifstat[ifp->if_index] == NULL) { in6_ifstat[ifp->if_index] = (struct in6_ifstat *) malloc(sizeof(struct in6_ifstat), M_IFADDR, M_WAITOK); bzero(in6_ifstat[ifp->if_index], sizeof(struct in6_ifstat)); } if (icmp6_ifstat[ifp->if_index] == NULL) { icmp6_ifstat[ifp->if_index] = (struct icmp6_ifstat *) malloc(sizeof(struct icmp6_ifstat), M_IFADDR, M_WAITOK); bzero(icmp6_ifstat[ifp->if_index], sizeof(struct icmp6_ifstat)); } /* initialize NDP variables */ nd6_ifattach(ifp); - - /* mark the address TENTATIVE, if needed. */ - switch (ifp->if_type) { - case IFT_ARCNET: - case IFT_ETHER: - case IFT_FDDI: - ia->ia6_flags |= IN6_IFF_TENTATIVE; - /* nd6_dad_start() will be called in in6_if_up */ - break; -#ifdef IFT_DUMMY - case IFT_DUMMY: -#endif - case IFT_GIF: /*XXX*/ - case IFT_LOOP: - case IFT_FAITH: - default: - break; - } - - return; } +/* + * NOTE: in6_ifdetach() does not support loopback if at this moment. + */ void in6_ifdetach(ifp) struct ifnet *ifp; { struct in6_ifaddr *ia, *oia; - struct ifaddr *ifa; + struct ifaddr *ifa, *next; struct rtentry *rt; short rtflags; + struct sockaddr_in6 sin6; + struct in6_multi *in6m; + struct in6_multi *in6m_next; - TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) + /* nuke prefix list. this may try to remove some of ifaddrs as well */ + in6_purgeprefix(ifp); + + /* remove neighbor management table */ + nd6_purge(ifp); + + /* nuke any of IPv6 addresses we have */ + for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = next) { + next = ifa->ifa_list.tqe_next; + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; + in6_purgeaddr(ifa, ifp); + } + + /* undo everything done by in6_ifattach(), just in case */ + for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = next) + { + next = ifa->ifa_list.tqe_next; + + if (ifa->ifa_addr->sa_family != AF_INET6 || !IN6_IS_ADDR_LINKLOCAL(&satosin6(&ifa->ifa_addr)->sin6_addr)) { continue; } ia = (struct in6_ifaddr *)ifa; /* remove from the routing table */ if ((ia->ia_flags & IFA_ROUTE) && (rt = rtalloc1((struct sockaddr *)&ia->ia_addr, 0, 0UL))) { rtflags = rt->rt_flags; rtfree(rt); rtrequest(RTM_DELETE, (struct sockaddr *)&ia->ia_addr, (struct sockaddr *)&ia->ia_addr, (struct sockaddr *)&ia->ia_prefixmask, rtflags, (struct rtentry **)0); } /* remove from the linked list */ TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list); + IFAFREE(&ia->ia_ifa); /* also remove from the IPv6 address chain(itojun&jinmei) */ oia = ia; if (oia == (ia = in6_ifaddr)) in6_ifaddr = ia->ia_next; else { while (ia->ia_next && (ia->ia_next != oia)) ia = ia->ia_next; if (ia->ia_next) ia->ia_next = oia->ia_next; -#ifdef DEBUG +#ifdef ND6_DEBUG else printf("%s: didn't unlink in6ifaddr from " "list\n", if_name(ifp)); #endif } - free(ia, M_IFADDR); + IFAFREE(&oia->ia_ifa); + } + + /* leave from all multicast groups joined */ + for (in6m = LIST_FIRST(&in6_multihead); in6m; in6m = in6m_next) { + in6m_next = LIST_NEXT(in6m, in6m_entry); + if (in6m->in6m_ifp != ifp) + continue; + in6_delmulti(in6m); + in6m = NULL; + } + + /* remove neighbor management table */ + nd6_purge(ifp); + + /* remove route to link-local allnodes multicast (ff02::1) */ + bzero(&sin6, sizeof(sin6)); + sin6.sin6_len = sizeof(struct sockaddr_in6); + sin6.sin6_family = AF_INET6; + sin6.sin6_addr = in6addr_linklocal_allnodes; + sin6.sin6_addr.s6_addr16[1] = htons(ifp->if_index); + if ((rt = rtalloc1((struct sockaddr *)&sin6, 0, 0UL)) != NULL) + { + rtrequest(RTM_DELETE, (struct sockaddr *)rt_key(rt), + rt->rt_gateway, rt_mask(rt), rt->rt_flags, 0); + rtfree(rt); } } Index: head/sys/netinet6/in6_ifattach.h =================================================================== --- head/sys/netinet6/in6_ifattach.h (revision 62586) +++ head/sys/netinet6/in6_ifattach.h (revision 62587) @@ -1,50 +1,43 @@ +/* $FreeBSD$ */ +/* $KAME: in6_ifattach.h,v 1.10 2000/05/27 02:57:05 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ #ifndef _NETINET6_IN6_IFATTACH_H_ -#define _NETINET6_IN6_IFATTACH_H_ +#define _NETINET6_IN6_IFATTACH_H_ #ifdef _KERNEL -extern int found_first_ifid; - -int in6_ifattach_getifid __P((struct ifnet *)); -void in6_ifattach_p2p __P((void)); -void in6_ifattach __P((struct ifnet *, u_int, caddr_t, int)); -void in6_ifdetach __P((struct ifnet *)); +void in6_nigroup_attach __P((const char *, int)); +void in6_nigroup_detach __P((const char *, int)); +void in6_ifattach __P((struct ifnet *, struct ifnet *)); +void in6_ifdetach __P((struct ifnet *)); #endif /* _KERNEL */ - -#define IN6_IFT_LOOP 1 -#define IN6_IFT_P2P 2 -#define IN6_IFT_802 3 -#define IN6_IFT_P2P802 4 -#define IN6_IFT_ARCNET 5 #endif /* _NETINET6_IN6_IFATTACH_H_ */ Index: head/sys/netinet6/in6_pcb.c =================================================================== --- head/sys/netinet6/in6_pcb.c (revision 62586) +++ head/sys/netinet6/in6_pcb.c (revision 62587) @@ -1,1167 +1,1061 @@ +/* $FreeBSD$ */ +/* $KAME: in6_pcb.c,v 1.8 2000/06/09 00:37:02 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD$ */ /* * Copyright (c) 1982, 1986, 1991, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)in_pcb.c 8.2 (Berkeley) 1/4/94 - * $FreeBSD$ */ #include "opt_ipsec.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include #include #include #include #include "faith.h" #ifdef IPSEC #include #include #include #include #include -#ifdef IPSEC_DEBUG -#include -#else -#define KEYDEBUG(lev,arg) -#endif /* IPSEC_DEBUG */ #endif /* IPSEC */ struct in6_addr zeroin6_addr; int in6_pcbbind(inp, nam, p) register struct inpcb *inp; struct sockaddr *nam; struct proc *p; { struct socket *so = inp->inp_socket; - unsigned short *lastport; struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)NULL; struct inpcbinfo *pcbinfo = inp->inp_pcbinfo; u_short lport = 0; int wild = 0, reuseport = (so->so_options & SO_REUSEPORT); - int error; if (!in6_ifaddr) /* XXX broken! */ return (EADDRNOTAVAIL); if (inp->inp_lport || !IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr)) return(EINVAL); if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0) wild = 1; if (nam) { sin6 = (struct sockaddr_in6 *)nam; if (nam->sa_len != sizeof(*sin6)) return(EINVAL); /* * family check. */ if (nam->sa_family != AF_INET6) return(EAFNOSUPPORT); - /* - * If the scope of the destination is link-local, embed the - * interface index in the address. - */ - if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr)) { - /* XXX boundary check is assumed to be already done. */ - /* XXX sin6_scope_id is weaker than advanced-api. */ - struct in6_pktinfo *pi; - if (inp->in6p_outputopts && - (pi = inp->in6p_outputopts->ip6po_pktinfo) && - pi->ipi6_ifindex) { - sin6->sin6_addr.s6_addr16[1] - = htons(pi->ipi6_ifindex); - } else if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr) - && inp->in6p_moptions - && inp->in6p_moptions->im6o_multicast_ifp) { - sin6->sin6_addr.s6_addr16[1] = - htons(inp->in6p_moptions->im6o_multicast_ifp->if_index); - } else if (sin6->sin6_scope_id) { - /* boundary check */ - if (sin6->sin6_scope_id < 0 - || if_index < sin6->sin6_scope_id) { - return ENXIO; /* XXX EINVAL? */ - } - sin6->sin6_addr.s6_addr16[1] - = htons(sin6->sin6_scope_id & 0xffff);/*XXX*/ - /* this must be cleared for ifa_ifwithaddr() */ - sin6->sin6_scope_id = 0; - } - } + /* KAME hack: embed scopeid */ + if (in6_embedscope(&sin6->sin6_addr, sin6, inp, NULL) != 0) + return EINVAL; + /* this must be cleared for ifa_ifwithaddr() */ + sin6->sin6_scope_id = 0; lport = sin6->sin6_port; if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) { /* * Treat SO_REUSEADDR as SO_REUSEPORT for multicast; * allow compepte duplication of binding if * SO_REUSEPORT is set, or if SO_REUSEADDR is set * and a multicast address is bound on both * new and duplicated sockets. */ if (so->so_options & SO_REUSEADDR) reuseport = SO_REUSEADDR|SO_REUSEPORT; } else if (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { struct ifaddr *ia = NULL; sin6->sin6_port = 0; /* yech... */ if ((ia = ifa_ifwithaddr((struct sockaddr *)sin6)) == 0) return(EADDRNOTAVAIL); /* * XXX: bind to an anycast address might accidentally * cause sending a packet with anycast source address. */ if (ia && ((struct in6_ifaddr *)ia)->ia6_flags & (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY| IN6_IFF_DETACHED|IN6_IFF_DEPRECATED)) { return(EADDRNOTAVAIL); } } if (lport) { struct inpcb *t; /* GROSS */ if (ntohs(lport) < IPV6PORT_RESERVED && p && suser_xxx(0, p, PRISON_ROOT)) return(EACCES); if (so->so_cred->cr_uid != 0 && !IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) { t = in6_pcblookup_local(pcbinfo, &sin6->sin6_addr, lport, INPLOOKUP_WILDCARD); if (t && (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) || !IN6_IS_ADDR_UNSPECIFIED(&t->in6p_laddr) || (t->inp_socket->so_options & SO_REUSEPORT) == 0) && (so->so_cred->cr_uid != t->inp_socket->so_cred->cr_uid)) return (EADDRINUSE); - if ((inp->inp_flags & IN6P_BINDV6ONLY) == 0 && + if (ip6_mapped_addr_on != 0 && IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { struct sockaddr_in sin; in6_sin6_2_sin(&sin, sin6); t = in_pcblookup_local(pcbinfo, sin.sin_addr, lport, INPLOOKUP_WILDCARD); if (t && (so->so_cred->cr_uid != t->inp_socket->so_cred->cr_uid) && (ntohl(t->inp_laddr.s_addr) != INADDR_ANY || INP_SOCKAF(so) == INP_SOCKAF(t->inp_socket))) return (EADDRINUSE); } } t = in6_pcblookup_local(pcbinfo, &sin6->sin6_addr, lport, wild); if (t && (reuseport & t->inp_socket->so_options) == 0) return(EADDRINUSE); - if ((inp->inp_flags & IN6P_BINDV6ONLY) == 0 && + if (ip6_mapped_addr_on != 0 && IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { struct sockaddr_in sin; in6_sin6_2_sin(&sin, sin6); t = in_pcblookup_local(pcbinfo, sin.sin_addr, lport, wild); if (t && (reuseport & t->inp_socket->so_options) == 0 && (ntohl(t->inp_laddr.s_addr) != INADDR_ANY || INP_SOCKAF(so) == INP_SOCKAF(t->inp_socket))) return (EADDRINUSE); } } inp->in6p_laddr = sin6->sin6_addr; } if (lport == 0) { - ushort first, last; - int count; - - inp->inp_flags |= INP_ANONPORT; - - if (inp->inp_flags & INP_HIGHPORT) { - first = ipport_hifirstauto; /* sysctl */ - last = ipport_hilastauto; - lastport = &pcbinfo->lasthi; - } else if (inp->inp_flags & INP_LOWPORT) { - if (p && (error = suser_xxx(0, p, PRISON_ROOT))) - return error; - first = ipport_lowfirstauto; /* 1023 */ - last = ipport_lowlastauto; /* 600 */ - lastport = &pcbinfo->lastlow; - } else { - first = ipport_firstauto; /* sysctl */ - last = ipport_lastauto; - lastport = &pcbinfo->lastport; + int e; + if ((e = in6_pcbsetport(&inp->in6p_laddr, inp, p)) != 0) + return(e); + } + else { + inp->inp_lport = lport; + if (in_pcbinshash(inp) != 0) { + inp->in6p_laddr = in6addr_any; + inp->inp_lport = 0; + return (EAGAIN); } - /* - * Simple check to ensure all ports are not used up causing - * a deadlock here. - * - * We split the two cases (up and down) so that the direction - * is not being tested on each round of the loop. - */ - if (first > last) { - /* - * counting down - */ - count = first - last; - - do { - if (count-- < 0) { /* completely used? */ - /* - * Undo any address bind that may have - * occurred above. - */ - inp->in6p_laddr = in6addr_any; - return (EAGAIN); - } - --*lastport; - if (*lastport > first || *lastport < last) - *lastport = first; - lport = htons(*lastport); - } while (in6_pcblookup_local(pcbinfo, - &inp->in6p_laddr, lport, wild)); - } else { - /* - * counting up - */ - count = last - first; - - do { - if (count-- < 0) { /* completely used? */ - /* - * Undo any address bind that may have - * occurred above. - */ - inp->in6p_laddr = in6addr_any; - return (EAGAIN); - } - ++*lastport; - if (*lastport < first || *lastport > last) - *lastport = first; - lport = htons(*lastport); - } while (in6_pcblookup_local(pcbinfo, - &inp->in6p_laddr, lport, wild)); - } } - inp->inp_lport = lport; - if (in_pcbinshash(inp) != 0) { - inp->in6p_laddr = in6addr_any; - inp->inp_lport = 0; - return (EAGAIN); - } inp->in6p_flowinfo = sin6 ? sin6->sin6_flowinfo : 0; /*XXX*/ return(0); } /* * Transform old in6_pcbconnect() into an inner subroutine for new * in6_pcbconnect(): Do some validity-checking on the remote * address (in mbuf 'nam') and then determine local host address * (i.e., which interface) to use to access that remote host. * * This preserves definition of in6_pcbconnect(), while supporting a * slightly different version for T/TCP. (This is more than * a bit of a kludge, but cleaning up the internal interfaces would * have forced minor changes in every protocol). */ int in6_pcbladdr(inp, nam, plocal_addr6) register struct inpcb *inp; struct sockaddr *nam; struct in6_addr **plocal_addr6; { register struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)nam; struct in6_pktinfo *pi; struct ifnet *ifp = NULL; int error = 0; if (nam->sa_len != sizeof (*sin6)) return (EINVAL); if (sin6->sin6_family != AF_INET6) return (EAFNOSUPPORT); if (sin6->sin6_port == 0) return (EADDRNOTAVAIL); - /* - * If the scope of the destination is link-local, embed the interface - * index in the address. - */ - if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr)) { - /* XXX boundary check is assumed to be already done. */ - /* XXX sin6_scope_id is weaker than advanced-api. */ - if (inp->in6p_outputopts && - (pi = inp->in6p_outputopts->ip6po_pktinfo) && - pi->ipi6_ifindex) { - sin6->sin6_addr.s6_addr16[1] = htons(pi->ipi6_ifindex); - ifp = ifindex2ifnet[pi->ipi6_ifindex]; - } else if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr) && - inp->in6p_moptions && - inp->in6p_moptions->im6o_multicast_ifp) { - sin6->sin6_addr.s6_addr16[1] = - htons(inp->in6p_moptions->im6o_multicast_ifp->if_index); - ifp = ifindex2ifnet[inp->in6p_moptions->im6o_multicast_ifp->if_index]; - } else if (sin6->sin6_scope_id) { - /* boundary check */ - if (sin6->sin6_scope_id < 0 - || if_index < sin6->sin6_scope_id) { - return ENXIO; /* XXX EINVAL? */ - } - sin6->sin6_addr.s6_addr16[1] - = htons(sin6->sin6_scope_id & 0xffff);/*XXX*/ - ifp = ifindex2ifnet[sin6->sin6_scope_id]; - } - } + /* KAME hack: embed scopeid */ + if (in6_embedscope(&sin6->sin6_addr, sin6, inp, &ifp) != 0) + return EINVAL; if (in6_ifaddr) { /* * If the destination address is UNSPECIFIED addr, * use the loopback addr, e.g ::1. */ if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) sin6->sin6_addr = in6addr_loopback; } { /* * XXX: in6_selectsrc might replace the bound local address * with the address specified by setsockopt(IPV6_PKTINFO). * Is it the intended behavior? */ *plocal_addr6 = in6_selectsrc(sin6, inp->in6p_outputopts, inp->in6p_moptions, &inp->in6p_route, &inp->in6p_laddr, &error); if (*plocal_addr6 == 0) { if (error == 0) error = EADDRNOTAVAIL; return(error); } /* * Don't do pcblookup call here; return interface in * plocal_addr6 * and exit to caller, that will do the lookup. */ } if (inp->in6p_route.ro_rt) ifp = inp->in6p_route.ro_rt->rt_ifp; - inp->in6p_ip6_hlim = (u_int8_t)in6_selecthlim(inp, ifp); - return(0); } /* * Outer subroutine: * Connect from a socket to a specified address. * Both address and port must be specified in argument sin. * If don't have a local address for this socket yet, * then pick one. */ int in6_pcbconnect(inp, nam, p) register struct inpcb *inp; struct sockaddr *nam; struct proc *p; { struct in6_addr *addr6; register struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)nam; int error; /* * Call inner routine, to assign local interface address. */ if ((error = in6_pcbladdr(inp, nam, &addr6)) != 0) return(error); if (in6_pcblookup_hash(inp->inp_pcbinfo, &sin6->sin6_addr, sin6->sin6_port, IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr) ? addr6 : &inp->in6p_laddr, inp->inp_lport, 0, NULL) != NULL) { return (EADDRINUSE); } if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr)) { if (inp->inp_lport == 0) { error = in6_pcbbind(inp, (struct sockaddr *)0, p); if (error) return (error); } inp->in6p_laddr = *addr6; } inp->in6p_faddr = sin6->sin6_addr; inp->inp_fport = sin6->sin6_port; /* * xxx kazu flowlabel is necessary for connect? * but if this line is missing, the garbage value remains. */ inp->in6p_flowinfo = sin6->sin6_flowinfo; if ((inp->in6p_flowinfo & IPV6_FLOWLABEL_MASK) == 0 && ip6_auto_flowlabel != 0) inp->in6p_flowinfo |= (htonl(ip6_flow_seq++) & IPV6_FLOWLABEL_MASK); in_pcbrehash(inp); return (0); } +#if 0 /* * Return an IPv6 address, which is the most appropriate for given * destination and user specified options. * If necessary, this function lookups the routing table and return * an entry to the caller for later use. */ struct in6_addr * in6_selectsrc(dstsock, opts, mopts, ro, laddr, errorp) struct sockaddr_in6 *dstsock; struct ip6_pktopts *opts; struct ip6_moptions *mopts; struct route_in6 *ro; struct in6_addr *laddr; int *errorp; { struct in6_addr *dst; struct in6_ifaddr *ia6 = 0; struct in6_pktinfo *pi = NULL; dst = &dstsock->sin6_addr; *errorp = 0; /* * If the source address is explicitly specified by the caller, * use it. */ if (opts && (pi = opts->ip6po_pktinfo) && !IN6_IS_ADDR_UNSPECIFIED(&pi->ipi6_addr)) return(&pi->ipi6_addr); /* * If the source address is not specified but the socket(if any) * is already bound, use the bound address. */ if (laddr && !IN6_IS_ADDR_UNSPECIFIED(laddr)) return(laddr); /* * If the caller doesn't specify the source address but * the outgoing interface, use an address associated with * the interface. */ if (pi && pi->ipi6_ifindex) { /* XXX boundary check is assumed to be already done. */ ia6 = in6_ifawithscope(ifindex2ifnet[pi->ipi6_ifindex], dst); if (ia6 == 0) { *errorp = EADDRNOTAVAIL; return(0); } return(&satosin6(&ia6->ia_addr)->sin6_addr); } /* * If the destination address is a link-local unicast address or * a multicast address, and if the outgoing interface is specified * by the sin6_scope_id filed, use an address associated with the * interface. * XXX: We're now trying to define more specific semantics of * sin6_scope_id field, so this part will be rewritten in * the near future. */ if ((IN6_IS_ADDR_LINKLOCAL(dst) || IN6_IS_ADDR_MULTICAST(dst)) && dstsock->sin6_scope_id) { /* * I'm not sure if boundary check for scope_id is done * somewhere... */ if (dstsock->sin6_scope_id < 0 || if_index < dstsock->sin6_scope_id) { *errorp = ENXIO; /* XXX: better error? */ return(0); } ia6 = in6_ifawithscope(ifindex2ifnet[dstsock->sin6_scope_id], dst); if (ia6 == 0) { *errorp = EADDRNOTAVAIL; return(0); } return(&satosin6(&ia6->ia_addr)->sin6_addr); } /* * If the destination address is a multicast address and * the outgoing interface for the address is specified * by the caller, use an address associated with the interface. * There is a sanity check here; if the destination has node-local * scope, the outgoing interfacde should be a loopback address. * Even if the outgoing interface is not specified, we also * choose a loopback interface as the outgoing interface. */ if (IN6_IS_ADDR_MULTICAST(dst)) { struct ifnet *ifp = mopts ? mopts->im6o_multicast_ifp : NULL; if (ifp == NULL && IN6_IS_ADDR_MC_NODELOCAL(dst)) { ifp = &loif[0]; } if (ifp) { ia6 = in6_ifawithscope(ifp, dst); if (ia6 == 0) { *errorp = EADDRNOTAVAIL; return(0); } return(&ia6->ia_addr.sin6_addr); } } /* * If the next hop address for the packet is specified * by caller, use an address associated with the route * to the next hop. */ { struct sockaddr_in6 *sin6_next; struct rtentry *rt; if (opts && opts->ip6po_nexthop) { sin6_next = satosin6(opts->ip6po_nexthop); rt = nd6_lookup(&sin6_next->sin6_addr, 1, NULL); if (rt) { ia6 = in6_ifawithscope(rt->rt_ifp, dst); if (ia6 == 0) ia6 = ifatoia6(rt->rt_ifa); } if (ia6 == 0) { *errorp = EADDRNOTAVAIL; return(0); } return(&satosin6(&ia6->ia_addr)->sin6_addr); } } /* * If route is known or can be allocated now, * our src addr is taken from the i/f, else punt. */ if (ro) { if (ro->ro_rt && !IN6_ARE_ADDR_EQUAL(&satosin6(&ro->ro_dst)->sin6_addr, dst)) { RTFREE(ro->ro_rt); ro->ro_rt = (struct rtentry *)0; } if (ro->ro_rt == (struct rtentry *)0 || ro->ro_rt->rt_ifp == (struct ifnet *)0) { /* No route yet, so try to acquire one */ bzero(&ro->ro_dst, sizeof(struct sockaddr_in6)); ro->ro_dst.sin6_family = AF_INET6; ro->ro_dst.sin6_len = sizeof(struct sockaddr_in6); ro->ro_dst.sin6_addr = *dst; if (IN6_IS_ADDR_MULTICAST(dst)) { ro->ro_rt = rtalloc1(&((struct route *)ro) ->ro_dst, 0, 0UL); } else { rtalloc((struct route *)ro); } } /* * in_pcbconnect() checks out IFF_LOOPBACK to skip using * the address. But we don't know why it does so. * It is necessary to ensure the scope even for lo0 * so doesn't check out IFF_LOOPBACK. */ if (ro->ro_rt) { ia6 = in6_ifawithscope(ro->ro_rt->rt_ifa->ifa_ifp, dst); if (ia6 == 0) /* xxx scope error ?*/ ia6 = ifatoia6(ro->ro_rt->rt_ifa); } if (ia6 == 0) { *errorp = EHOSTUNREACH; /* no route */ return(0); } return(&satosin6(&ia6->ia_addr)->sin6_addr); } *errorp = EADDRNOTAVAIL; return(0); } /* * Default hop limit selection. The precedence is as follows: * 1. Hoplimit valued specified via ioctl. * 2. (If the outgoing interface is detected) the current * hop limit of the interface specified by router advertisement. * 3. The system default hoplimit. */ int in6_selecthlim(in6p, ifp) struct in6pcb *in6p; struct ifnet *ifp; { if (in6p && in6p->in6p_hops >= 0) return(in6p->in6p_hops); else if (ifp) return(nd_ifinfo[ifp->if_index].chlim); else return(ip6_defhlim); } +#endif void in6_pcbdisconnect(inp) struct inpcb *inp; { bzero((caddr_t)&inp->in6p_faddr, sizeof(inp->in6p_faddr)); inp->inp_fport = 0; in_pcbrehash(inp); if (inp->inp_socket->so_state & SS_NOFDREF) in6_pcbdetach(inp); } void in6_pcbdetach(inp) struct inpcb *inp; { struct socket *so = inp->inp_socket; struct inpcbinfo *ipi = inp->inp_pcbinfo; #ifdef IPSEC if (inp->in6p_sp != NULL) ipsec6_delete_pcbpolicy(inp); #endif /* IPSEC */ inp->inp_gencnt = ++ipi->ipi_gencnt; in_pcbremlists(inp); sotoinpcb(so) = 0; sofree(so); if (inp->in6p_options) m_freem(inp->in6p_options); if (inp->in6p_outputopts) { if (inp->in6p_outputopts->ip6po_rthdr && inp->in6p_outputopts->ip6po_route.ro_rt) RTFREE(inp->in6p_outputopts->ip6po_route.ro_rt); if (inp->in6p_outputopts->ip6po_m) (void)m_free(inp->in6p_outputopts->ip6po_m); free(inp->in6p_outputopts, M_IP6OPT); } if (inp->in6p_route.ro_rt) rtfree(inp->in6p_route.ro_rt); ip6_freemoptions(inp->in6p_moptions); /* Check and free IPv4 related resources in case of mapped addr */ if (inp->inp_options) (void)m_free(inp->inp_options); ip_freemoptions(inp->inp_moptions); inp->inp_vflag = 0; zfreei(ipi->ipi_zone, inp); } /* * The calling convention of in6_setsockaddr() and in6_setpeeraddr() was * modified to match the pru_sockaddr() and pru_peeraddr() entry points * in struct pr_usrreqs, so that protocols can just reference then directly * without the need for a wrapper function. The socket must have a valid * (i.e., non-nil) PCB, but it should be impossible to get an invalid one * except through a kernel programming error, so it is acceptable to panic * (or in this case trap) if the PCB is invalid. (Actually, we don't trap * because there actually /is/ a programming error somewhere... XXX) */ int in6_setsockaddr(so, nam) struct socket *so; struct sockaddr **nam; { int s; register struct inpcb *inp; register struct sockaddr_in6 *sin6; /* * Do the malloc first in case it blocks. */ MALLOC(sin6, struct sockaddr_in6 *, sizeof *sin6, M_SONAME, M_WAITOK); bzero(sin6, sizeof *sin6); sin6->sin6_family = AF_INET6; sin6->sin6_len = sizeof(*sin6); s = splnet(); inp = sotoinpcb(so); if (!inp) { splx(s); free(sin6, M_SONAME); return EINVAL; } sin6->sin6_port = inp->inp_lport; sin6->sin6_addr = inp->in6p_laddr; splx(s); if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr)) sin6->sin6_scope_id = ntohs(sin6->sin6_addr.s6_addr16[1]); else sin6->sin6_scope_id = 0; /*XXX*/ if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr)) sin6->sin6_addr.s6_addr16[1] = 0; *nam = (struct sockaddr *)sin6; return 0; } int in6_setpeeraddr(so, nam) struct socket *so; struct sockaddr **nam; { int s; struct inpcb *inp; register struct sockaddr_in6 *sin6; /* * Do the malloc first in case it blocks. */ MALLOC(sin6, struct sockaddr_in6 *, sizeof(*sin6), M_SONAME, M_WAITOK); bzero((caddr_t)sin6, sizeof (*sin6)); sin6->sin6_family = AF_INET6; sin6->sin6_len = sizeof(struct sockaddr_in6); s = splnet(); inp = sotoinpcb(so); if (!inp) { splx(s); free(sin6, M_SONAME); return EINVAL; } sin6->sin6_port = inp->inp_fport; sin6->sin6_addr = inp->in6p_faddr; splx(s); if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr)) sin6->sin6_scope_id = ntohs(sin6->sin6_addr.s6_addr16[1]); else sin6->sin6_scope_id = 0; /*XXX*/ if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr)) sin6->sin6_addr.s6_addr16[1] = 0; *nam = (struct sockaddr *)sin6; return 0; } int in6_mapped_sockaddr(struct socket *so, struct sockaddr **nam) { struct inpcb *inp = sotoinpcb(so); int error; if (inp == NULL) return EINVAL; if (inp->inp_vflag & INP_IPV4) { error = in_setsockaddr(so, nam); if (error == 0) in6_sin_2_v4mapsin6_in_sock(nam); } else error = in6_setsockaddr(so, nam); return error; } int in6_mapped_peeraddr(struct socket *so, struct sockaddr **nam) { struct inpcb *inp = sotoinpcb(so); int error; if (inp == NULL) return EINVAL; if (inp->inp_vflag & INP_IPV4) { error = in_setpeeraddr(so, nam); if (error == 0) in6_sin_2_v4mapsin6_in_sock(nam); } else error = in6_setpeeraddr(so, nam); return error; } /* * Pass some notification to all connections of a protocol * associated with address dst. The local address and/or port numbers * may be specified to limit the search. The "usual action" will be * taken, depending on the ctlinput cmd. The caller must filter any * cmds that are uninteresting (e.g., no error in the map). * Call the protocol specific routine (if any) to report * any errors for each matching socket. * * Must be called at splnet. */ void in6_pcbnotify(head, dst, fport_arg, laddr6, lport_arg, cmd, notify) struct inpcbhead *head; struct sockaddr *dst; u_int fport_arg, lport_arg; struct in6_addr *laddr6; int cmd; void (*notify) __P((struct inpcb *, int)); { - struct inpcb *inp, *oinp; + struct inpcb *inp, *ninp; struct in6_addr faddr6; u_short fport = fport_arg, lport = lport_arg; int errno, s; + int do_rtchange = (notify == in6_rtchange); if ((unsigned)cmd > PRC_NCMDS || dst->sa_family != AF_INET6) return; faddr6 = ((struct sockaddr_in6 *)dst)->sin6_addr; if (IN6_IS_ADDR_UNSPECIFIED(&faddr6)) return; /* * Redirects go to all references to the destination, - * and use in_rtchange to invalidate the route cache. - * Dead host indications: notify all references to the destination. + * and use in6_rtchange to invalidate the route cache. + * Dead host indications: also use in6_rtchange to invalidate + * the cache, and deliver the error to all the sockets. * Otherwise, if we have knowledge of the local port and address, * deliver only to that socket. */ if (PRC_IS_REDIRECT(cmd) || cmd == PRC_HOSTDEAD) { fport = 0; lport = 0; bzero((caddr_t)laddr6, sizeof(*laddr6)); - if (cmd != PRC_HOSTDEAD) - notify = in6_rtchange; + + do_rtchange = 1; } errno = inet6ctlerrmap[cmd]; s = splnet(); - for (inp = LIST_FIRST(head); inp != NULL;) { - if ((inp->inp_vflag & INP_IPV6) == 0) { - inp = LIST_NEXT(inp, inp_list); + for (inp = LIST_FIRST(head); inp != NULL; inp = ninp) { + ninp = LIST_NEXT(inp, inp_list); + + if ((inp->inp_vflag & INP_IPV6) == NULL) continue; - } + + if (do_rtchange) { + /* + * Since a non-connected PCB might have a cached route, + * we always call in6_rtchange without matching + * the PCB to the src/dst pair. + * + * XXX: we assume in6_rtchange does not free the PCB. + */ + if (IN6_ARE_ADDR_EQUAL(&inp->in6p_route.ro_dst.sin6_addr, + &faddr6)) + in6_rtchange(inp, errno); + + if (notify == in6_rtchange) + continue; /* there's nothing to do any more */ + } + if (!IN6_ARE_ADDR_EQUAL(&inp->in6p_faddr, &faddr6) || inp->inp_socket == 0 || (lport && inp->inp_lport != lport) || (!IN6_IS_ADDR_UNSPECIFIED(laddr6) && !IN6_ARE_ADDR_EQUAL(&inp->in6p_laddr, laddr6)) || - (fport && inp->inp_fport != fport)) { - inp = LIST_NEXT(inp, inp_list); + (fport && inp->inp_fport != fport)) continue; - } - oinp = inp; - inp = LIST_NEXT(inp, inp_list); + if (notify) - (*notify)(oinp, errno); + (*notify)(inp, errno); } splx(s); } /* * Lookup a PCB based on the local address and port. */ struct inpcb * in6_pcblookup_local(pcbinfo, laddr, lport_arg, wild_okay) struct inpcbinfo *pcbinfo; struct in6_addr *laddr; u_int lport_arg; int wild_okay; { register struct inpcb *inp; int matchwild = 3, wildcard; u_short lport = lport_arg; if (!wild_okay) { struct inpcbhead *head; /* * Look for an unconnected (wildcard foreign addr) PCB that * matches the local address and port we're looking for. */ head = &pcbinfo->hashbase[INP_PCBHASH(INADDR_ANY, lport, 0, pcbinfo->hashmask)]; LIST_FOREACH(inp, head, inp_hash) { if ((inp->inp_vflag & INP_IPV6) == 0) continue; if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr) && IN6_ARE_ADDR_EQUAL(&inp->in6p_laddr, laddr) && inp->inp_lport == lport) { /* * Found. */ return (inp); } } /* * Not found. */ return (NULL); } else { struct inpcbporthead *porthash; struct inpcbport *phd; struct inpcb *match = NULL; /* * Best fit PCB lookup. * * First see if this local port is in use by looking on the * port hash list. */ porthash = &pcbinfo->porthashbase[INP_PCBPORTHASH(lport, pcbinfo->porthashmask)]; LIST_FOREACH(phd, porthash, phd_hash) { if (phd->phd_port == lport) break; } if (phd != NULL) { /* * Port is in use by one or more PCBs. Look for best * fit. */ LIST_FOREACH(inp, &phd->phd_pcblist, inp_portlist) { wildcard = 0; if ((inp->inp_vflag & INP_IPV6) == 0) continue; if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) wildcard++; if (!IN6_IS_ADDR_UNSPECIFIED( &inp->in6p_laddr)) { if (IN6_IS_ADDR_UNSPECIFIED(laddr)) wildcard++; else if (!IN6_ARE_ADDR_EQUAL( &inp->in6p_laddr, laddr)) continue; } else { if (!IN6_IS_ADDR_UNSPECIFIED(laddr)) wildcard++; } if (wildcard < matchwild) { match = inp; matchwild = wildcard; if (matchwild == 0) { break; } } } } return (match); } } /* * Check for alternatives when higher level complains * about service problems. For now, invalidate cached * routing information. If the route was created dynamically * (by a redirect), time to try a default gateway again. */ void in6_losing(in6p) struct inpcb *in6p; { struct rtentry *rt; struct rt_addrinfo info; if ((rt = in6p->in6p_route.ro_rt) != NULL) { in6p->in6p_route.ro_rt = 0; bzero((caddr_t)&info, sizeof(info)); info.rti_info[RTAX_DST] = (struct sockaddr *)&in6p->in6p_route.ro_dst; info.rti_info[RTAX_GATEWAY] = rt->rt_gateway; info.rti_info[RTAX_NETMASK] = rt_mask(rt); rt_missmsg(RTM_LOSING, &info, rt->rt_flags, 0); if (rt->rt_flags & RTF_DYNAMIC) (void)rtrequest(RTM_DELETE, rt_key(rt), rt->rt_gateway, rt_mask(rt), rt->rt_flags, (struct rtentry **)0); else /* * A new route can be allocated * the next time output is attempted. */ rtfree(rt); } } /* * After a routing change, flush old routing * and allocate a (hopefully) better one. */ void in6_rtchange(inp, errno) struct inpcb *inp; int errno; { if (inp->in6p_route.ro_rt) { rtfree(inp->in6p_route.ro_rt); inp->in6p_route.ro_rt = 0; /* * A new route can be allocated the next time * output is attempted. */ } } /* * Lookup PCB in hash list. */ struct inpcb * in6_pcblookup_hash(pcbinfo, faddr, fport_arg, laddr, lport_arg, wildcard, ifp) struct inpcbinfo *pcbinfo; struct in6_addr *faddr, *laddr; u_int fport_arg, lport_arg; int wildcard; struct ifnet *ifp; { struct inpcbhead *head; register struct inpcb *inp; u_short fport = fport_arg, lport = lport_arg; /* * First look for an exact match. */ head = &pcbinfo->hashbase[INP_PCBHASH(faddr->s6_addr32[3] /* XXX */, lport, fport, pcbinfo->hashmask)]; LIST_FOREACH(inp, head, inp_hash) { if ((inp->inp_vflag & INP_IPV6) == 0) continue; if (IN6_ARE_ADDR_EQUAL(&inp->in6p_faddr, faddr) && IN6_ARE_ADDR_EQUAL(&inp->in6p_laddr, laddr) && inp->inp_fport == fport && inp->inp_lport == lport) { /* * Found. */ return (inp); } } if (wildcard) { struct inpcb *local_wild = NULL; head = &pcbinfo->hashbase[INP_PCBHASH(INADDR_ANY, lport, 0, pcbinfo->hashmask)]; LIST_FOREACH(inp, head, inp_hash) { if ((inp->inp_vflag & INP_IPV6) == 0) continue; if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr) && inp->inp_lport == lport) { #if defined(NFAITH) && NFAITH > 0 if (ifp && ifp->if_type == IFT_FAITH && (inp->inp_flags & INP_FAITH) == 0) continue; #endif if (IN6_ARE_ADDR_EQUAL(&inp->in6p_laddr, laddr)) return (inp); else if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr)) local_wild = inp; } } return (local_wild); } /* * Not found. */ return (NULL); } void init_sin6(struct sockaddr_in6 *sin6, struct mbuf *m) { struct ip6_hdr *ip; ip = mtod(m, struct ip6_hdr *); bzero(sin6, sizeof(*sin6)); sin6->sin6_len = sizeof(*sin6); sin6->sin6_family = AF_INET6; sin6->sin6_addr = ip->ip6_src; if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr)) sin6->sin6_addr.s6_addr16[1] = 0; sin6->sin6_scope_id = (m->m_pkthdr.rcvif && IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr)) ? m->m_pkthdr.rcvif->if_index : 0; return; } Index: head/sys/netinet6/in6_pcb.h =================================================================== --- head/sys/netinet6/in6_pcb.h (revision 62586) +++ head/sys/netinet6/in6_pcb.h (revision 62587) @@ -1,108 +1,115 @@ +/* $FreeBSD$ */ +/* $KAME: in6_pcb.h,v 1.5 2000/07/03 06:19:53 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD$ */ /* * Copyright (c) 1982, 1986, 1990, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)in_pcb.h 8.1 (Berkeley) 6/10/93 */ #ifndef _NETINET6_IN6_PCB_H_ #define _NETINET6_IN6_PCB_H_ #ifdef _KERNEL #define satosin6(sa) ((struct sockaddr_in6 *)(sa)) #define sin6tosa(sin6) ((struct sockaddr *)(sin6)) #define ifatoia6(ifa) ((struct in6_ifaddr *)(ifa)) void in6_losing __P((struct inpcb *)); int in6_pcballoc __P((struct socket *, struct inpcbinfo *, struct proc *)); int in6_pcbbind __P((struct inpcb *, struct sockaddr *, struct proc *)); int in6_pcbconnect __P((struct inpcb *, struct sockaddr *, struct proc *)); void in6_pcbdetach __P((struct inpcb *)); void in6_pcbdisconnect __P((struct inpcb *)); int in6_pcbladdr __P((struct inpcb *, struct sockaddr *, struct in6_addr **)); struct inpcb * in6_pcblookup_local __P((struct inpcbinfo *, struct in6_addr *, u_int, int)); struct inpcb * in6_pcblookup_hash __P((struct inpcbinfo *, struct in6_addr *, u_int, struct in6_addr *, u_int, int, struct ifnet *)); void in6_pcbnotify __P((struct inpcbhead *, struct sockaddr *, u_int, struct in6_addr *, u_int, int, void (*)(struct inpcb *, int))); void in6_rtchange __P((struct inpcb *, int)); int in6_setpeeraddr __P((struct socket *so, struct sockaddr **nam)); int in6_setsockaddr __P((struct socket *so, struct sockaddr **nam)); int in6_mapped_sockaddr __P((struct socket *so, struct sockaddr **nam)); int in6_mapped_peeraddr __P((struct socket *so, struct sockaddr **nam)); struct in6_addr *in6_selectsrc __P((struct sockaddr_in6 *, struct ip6_pktopts *, struct ip6_moptions *, struct route_in6 *, struct in6_addr *, int *)); -int in6_selecthlim __P((struct inpcb *, struct ifnet *)); - +int in6_selecthlim __P((struct in6pcb *, struct ifnet *)); +int in6_pcbsetport __P((struct in6_addr *, struct inpcb *, struct proc *)); void init_sin6 __P((struct sockaddr_in6 *sin6, struct mbuf *m)); + +int in6_embedscope __P((struct in6_addr *, const struct sockaddr_in6 *, + struct inpcb *, struct ifnet **)); +int in6_recoverscope __P((struct sockaddr_in6 *, const struct in6_addr *, + struct ifnet *)); #endif /* _KERNEL */ #endif /* !_NETINET6_IN6_PCB_H_ */ Index: head/sys/netinet6/in6_prefix.c =================================================================== --- head/sys/netinet6/in6_prefix.c (revision 62586) +++ head/sys/netinet6/in6_prefix.c (revision 62587) @@ -1,1142 +1,1186 @@ +/* $FreeBSD$ */ +/* $KAME: in6_prefix.c,v 1.30 2000/06/12 14:53:17 jinmei Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* * Copyright (c) 1982, 1986, 1991, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)in.c 8.2 (Berkeley) 11/15/93 */ #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include static MALLOC_DEFINE(M_IP6RR, "ip6rr", "IPv6 Router Renumbering Prefix"); static MALLOC_DEFINE(M_RR_ADDR, "rp_addr", "IPv6 Router Renumbering Ifid"); struct rr_prhead rr_prefix; #include static void add_each_addr __P((struct socket *so, struct rr_prefix *rpp, struct rp_addr *rap)); -static int create_ra_entry __P((struct rp_addr **rapp)); -static int add_each_prefix __P((struct socket *so, - struct rr_prefix *rpp)); -static void free_rp_entries __P((struct rr_prefix *rpp)); -static int link_stray_ia6s __P((struct rr_prefix *rpp)); +static int create_ra_entry __P((struct rp_addr **rapp)); +static int add_each_prefix __P((struct socket *so, struct rr_prefix *rpp)); +static void free_rp_entries __P((struct rr_prefix *rpp)); +static int link_stray_ia6s __P((struct rr_prefix *rpp)); static void rp_remove __P((struct rr_prefix *rpp)); /* * Copy bits from src to tgt, from off bit for len bits. * Caller must specify collect tgtsize and srcsize. */ static void bit_copy(char *tgt, u_int tgtsize, char *src, u_int srcsize, u_int off, u_int len) { char *sp, *tp; /* arg values check */ if (srcsize < off || srcsize < (off + len) || tgtsize < off || tgtsize < (off + len)) { log(LOG_ERR, "in6_prefix.c: bit_copy: invalid args: srcsize %d,\n" "tgtsize %d, off %d, len %d\n", srcsize, tgtsize, off, len); return; } /* search start point */ for (sp = src, tp = tgt; off >= 8; sp++, tp++) off-=8; /* copy starting bits */ if (off) { char setbit; int startbits; startbits = min((8 - off), len); for (setbit = (0x80 >> off); startbits; setbit >>= 1, startbits--, len--) *tp |= (setbit & *sp); tp++; sp++; } /* copy midium bits */ for (; len >= 8; sp++, tp++) { *tp = *sp; len-=8; } /* copy ending bits */ if (len) { char setbit; for (setbit = 0x80; len; setbit >>= 1, len--) *tp |= (setbit & *sp); } } static struct ifprefix * in6_prefixwithifp(struct ifnet *ifp, int plen, struct in6_addr *dst) { struct ifprefix *ifpr; /* search matched prefix */ for (ifpr = TAILQ_FIRST(&ifp->if_prefixhead); ifpr; - ifpr = TAILQ_NEXT(ifpr, ifpr_list)) { + ifpr = TAILQ_NEXT(ifpr, ifpr_list)) + { if (ifpr->ifpr_prefix->sa_family != AF_INET6 || ifpr->ifpr_type != IN6_PREFIX_RR) continue; if (plen <= in6_matchlen(dst, IFPR_IN6(ifpr))) break; } return (ifpr); } /* * Search prefix which matches arg prefix as specified in * draft-ietf-ipngwg-router-renum-08.txt */ static struct rr_prefix * search_matched_prefix(struct ifnet *ifp, struct in6_prefixreq *ipr) { struct ifprefix *ifpr; struct ifaddr *ifa; struct rr_prefix *rpp; /* search matched prefix */ ifpr = in6_prefixwithifp(ifp, ipr->ipr_plen, &ipr->ipr_prefix.sin6_addr); if (ifpr != NULL) return ifpr2rp(ifpr); /* * search matched addr, and then search prefix * which matches the addr */ TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; if (ipr->ipr_plen <= in6_matchlen(&ipr->ipr_prefix.sin6_addr, IFA_IN6(ifa))) break; } if (ifa == NULL) return NULL; rpp = ifpr2rp(((struct in6_ifaddr *)ifa)->ia6_ifpr); if (rpp != 0) return rpp; for (ifpr = TAILQ_FIRST(&ifp->if_prefixhead); ifpr; - ifpr = TAILQ_NEXT(ifpr, ifpr_list)) { + ifpr = TAILQ_NEXT(ifpr, ifpr_list)) + { if (ifpr->ifpr_prefix->sa_family != AF_INET6 || ifpr->ifpr_type != IN6_PREFIX_RR) continue; if (ifpr->ifpr_plen <= in6_matchlen(IFA_IN6(ifa), IFPR_IN6(ifpr))) break; } if (ifpr != NULL) log(LOG_ERR, "in6_prefix.c: search_matched_prefix: addr %s" - "has no pointer to prefix %s", ip6_sprintf(IFA_IN6(ifa)), + "has no pointer to prefix %s\n", ip6_sprintf(IFA_IN6(ifa)), ip6_sprintf(IFPR_IN6(ifpr))); return ifpr2rp(ifpr); } /* * Search prefix which matches arg prefix as specified in * draft-ietf-ipngwg-router-renum-08.txt, and mark it if exists. * Return 1 if anything matched, and 0 if nothing matched. */ static int mark_matched_prefixes(u_long cmd, struct ifnet *ifp, struct in6_rrenumreq *irr) { struct ifprefix *ifpr; struct ifaddr *ifa; int matchlen, matched = 0; /* search matched prefixes */ for (ifpr = TAILQ_FIRST(&ifp->if_prefixhead); ifpr; - ifpr = TAILQ_NEXT(ifpr, ifpr_list)) { + ifpr = TAILQ_NEXT(ifpr, ifpr_list)) + { if (ifpr->ifpr_prefix->sa_family != AF_INET6 || ifpr->ifpr_type != IN6_PREFIX_RR) continue; matchlen = in6_matchlen(&irr->irr_matchprefix.sin6_addr, IFPR_IN6(ifpr)); if (irr->irr_m_minlen > ifpr->ifpr_plen || irr->irr_m_maxlen < ifpr->ifpr_plen || irr->irr_m_len > matchlen) continue; matched = 1; ifpr2rp(ifpr)->rp_statef_addmark = 1; if (cmd == SIOCCIFPREFIX_IN6) ifpr2rp(ifpr)->rp_statef_delmark = 1; } /* * search matched addr, and then search prefixes * which matche the addr */ TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) { struct rr_prefix *rpp; if (ifa->ifa_addr->sa_family != AF_INET6) continue; matchlen = in6_matchlen(&irr->irr_matchprefix.sin6_addr, IFA_IN6(ifa)); if (irr->irr_m_minlen > matchlen || irr->irr_m_maxlen < matchlen || irr->irr_m_len > matchlen) continue; rpp = ifpr2rp(((struct in6_ifaddr *)ifa)->ia6_ifpr); if (rpp != 0) { matched = 1; rpp->rp_statef_addmark = 1; if (cmd == SIOCCIFPREFIX_IN6) rpp->rp_statef_delmark = 1; } else log(LOG_WARNING, "in6_prefix.c: mark_matched_prefixes:" "no back pointer to ifprefix for %s. " - "ND autoconfigured addr?", + "ND autoconfigured addr?\n", ip6_sprintf(IFA_IN6(ifa))); } return matched; } /* * Mark global prefixes as to be deleted. */ static void delmark_global_prefixes(struct ifnet *ifp, struct in6_rrenumreq *irr) { struct ifprefix *ifpr; /* search matched prefixes */ for (ifpr = TAILQ_FIRST(&ifp->if_prefixhead); ifpr; - ifpr = TAILQ_NEXT(ifpr, ifpr_list)) { + ifpr = TAILQ_NEXT(ifpr, ifpr_list)) + { if (ifpr->ifpr_prefix->sa_family != AF_INET6 || ifpr->ifpr_type != IN6_PREFIX_RR) continue; /* mark delete global prefix */ if (in6_addrscope(RP_IN6(ifpr2rp(ifpr))) == IPV6_ADDR_SCOPE_GLOBAL) ifpr2rp(ifpr)->rp_statef_delmark = 1; } } /* Unmark prefixes */ static void unmark_prefixes(struct ifnet *ifp) { struct ifprefix *ifpr; /* unmark all prefix */ for (ifpr = TAILQ_FIRST(&ifp->if_prefixhead); ifpr; - ifpr = TAILQ_NEXT(ifpr, ifpr_list)) { + ifpr = TAILQ_NEXT(ifpr, ifpr_list)) + { if (ifpr->ifpr_prefix->sa_family != AF_INET6 || ifpr->ifpr_type != IN6_PREFIX_RR) continue; /* unmark prefix */ ifpr2rp(ifpr)->rp_statef_addmark = 0; ifpr2rp(ifpr)->rp_statef_delmark = 0; } } static void init_prefix_ltimes(struct rr_prefix *rpp) { + if (rpp->rp_pltime == RR_INFINITE_LIFETIME || rpp->rp_rrf_decrprefd == 0) rpp->rp_preferred = 0; else rpp->rp_preferred = time_second + rpp->rp_pltime; if (rpp->rp_vltime == RR_INFINITE_LIFETIME || rpp->rp_rrf_decrvalid == 0) rpp->rp_expire = 0; else rpp->rp_expire = time_second + rpp->rp_vltime; } static int rr_are_ifid_equal(struct in6_addr *ii1, struct in6_addr *ii2, int ii_len) { int ii_bytelen, ii_bitlen; int p_bytelen, p_bitlen; /* sanity check */ if (1 > ii_len || ii_len > 124) { /* as RFC2373, prefix is at least 4 bit */ log(LOG_ERR, "rr_are_ifid_equal: invalid ifid length(%d)\n", ii_len); return(0); } ii_bytelen = ii_len / 8; ii_bitlen = ii_len % 8; p_bytelen = sizeof(struct in6_addr) - ii_bytelen - 1; p_bitlen = 8 - ii_bitlen; if (bcmp(ii1->s6_addr + p_bytelen + 1, ii2->s6_addr + p_bytelen + 1, ii_bytelen)) return(0); if (((ii1->s6_addr[p_bytelen] << p_bitlen) & 0xff) != ((ii2->s6_addr[p_bytelen] << p_bitlen) & 0xff)) return(0); return(1); } static struct rp_addr * search_ifidwithprefix(struct rr_prefix *rpp, struct in6_addr *ifid) { struct rp_addr *rap; LIST_FOREACH(rap, &rpp->rp_addrhead, ra_entry) + { if (rr_are_ifid_equal(ifid, &rap->ra_ifid, (sizeof(struct in6_addr) << 3) - rpp->rp_plen)) break; + } return rap; } static int -assigne_ra_entry(struct rr_prefix *rpp, int iilen, struct in6_ifaddr *ia) +assign_ra_entry(struct rr_prefix *rpp, int iilen, struct in6_ifaddr *ia) { int error = 0; struct rp_addr *rap; int s; if ((error = create_ra_entry(&rap)) != 0) return error; /* copy interface id part */ bit_copy((caddr_t)&rap->ra_ifid, sizeof(rap->ra_ifid) << 3, (caddr_t)IA6_IN6(ia), sizeof(*IA6_IN6(ia)) << 3, rpp->rp_plen, iilen); /* link to ia, and put into list */ rap->ra_addr = ia; - /* - * Can't point rp2ifpr(rpp) from ia->ia6_ifpr now, - * because rpp may be on th stack. should fix it? - */ + rap->ra_addr->ia_ifa.ifa_refcnt++; +#if 0 /* Can't do this now, because rpp may be on th stack. should fix it? */ + ia->ia6_ifpr = rp2ifpr(rpp); +#endif s = splnet(); LIST_INSERT_HEAD(&rpp->rp_addrhead, rap, ra_entry); splx(s); return 0; } +/* + * add a link-local address to an interface. we will add new interface address + * (prefix database + new interface id). + */ static int in6_prefix_add_llifid(int iilen, struct in6_ifaddr *ia) { struct rr_prefix *rpp; struct rp_addr *rap; struct socket so; int error, s; if ((error = create_ra_entry(&rap)) != 0) return(error); /* copy interface id part */ bit_copy((caddr_t)&rap->ra_ifid, sizeof(rap->ra_ifid) << 3, (caddr_t)IA6_IN6(ia), sizeof(*IA6_IN6(ia)) << 3, 64, (sizeof(rap->ra_ifid) << 3) - 64); /* XXX: init dummy so */ bzero(&so, sizeof(so)); /* insert into list */ - LIST_FOREACH(rpp, &rr_prefix, rp_entry) { + LIST_FOREACH(rpp, &rr_prefix, rp_entry) + { + /* + * do not attempt to add an address, if ifp does not match + */ + if (rpp->rp_ifp != ia->ia_ifp) + continue; + s = splnet(); LIST_INSERT_HEAD(&rpp->rp_addrhead, rap, ra_entry); splx(s); add_each_addr(&so, rpp, rap); } return 0; } - +/* + * add an address to an interface. if the interface id portion is new, + * we will add new interface address (prefix database + new interface id). + */ int in6_prefix_add_ifid(int iilen, struct in6_ifaddr *ia) { int plen = (sizeof(*IA6_IN6(ia)) << 3) - iilen; struct ifprefix *ifpr; struct rp_addr *rap; int error = 0; if (IN6_IS_ADDR_LINKLOCAL(IA6_IN6(ia))) return(in6_prefix_add_llifid(iilen, ia)); ifpr = in6_prefixwithifp(ia->ia_ifp, plen, IA6_IN6(ia)); if (ifpr == NULL) { struct rr_prefix rp; struct socket so; int pplen = (plen == 128) ? 64 : plen; /* allocate a prefix for ia, with default properties */ /* init rp */ bzero(&rp, sizeof(rp)); rp.rp_type = IN6_PREFIX_RR; rp.rp_ifp = ia->ia_ifp; rp.rp_plen = pplen; rp.rp_prefix.sin6_len = sizeof(rp.rp_prefix); rp.rp_prefix.sin6_family = AF_INET6; bit_copy((char *)RP_IN6(&rp), sizeof(*RP_IN6(&rp)) << 3, (char *)&ia->ia_addr.sin6_addr, sizeof(ia->ia_addr.sin6_addr) << 3, 0, pplen); rp.rp_vltime = rp.rp_pltime = RR_INFINITE_LIFETIME; rp.rp_raf_onlink = 1; rp.rp_raf_auto = 1; /* Is some FlagMasks for rrf necessary? */ rp.rp_rrf_decrvalid = rp.rp_rrf_decrprefd = 0; rp.rp_origin = PR_ORIG_RR; /* can be renumbered */ /* create ra_entry */ error = link_stray_ia6s(&rp); if (error != 0) { free_rp_entries(&rp); return error; } /* XXX: init dummy so */ bzero(&so, sizeof(so)); error = add_each_prefix(&so, &rp); /* free each rp_addr entry */ free_rp_entries(&rp); if (error != 0) return error; /* search again */ ifpr = in6_prefixwithifp(ia->ia_ifp, pplen, IA6_IN6(ia)); if (ifpr == NULL) return 0; } rap = search_ifidwithprefix(ifpr2rp(ifpr), IA6_IN6(ia)); if (rap != NULL) { - if (rap->ra_addr == NULL) + if (rap->ra_addr == NULL) { rap->ra_addr = ia; - else if (rap->ra_addr != ia) { + rap->ra_addr->ia_ifa.ifa_refcnt++; + } else if (rap->ra_addr != ia) { /* There may be some inconsistencies between addrs. */ log(LOG_ERR, "ip6_prefix.c: addr %s/%d matched prefix" - "has already another ia %p(%s) on its ifid list", + "has already another ia %p(%s) on its ifid list\n", ip6_sprintf(IA6_IN6(ia)), plen, rap->ra_addr, ip6_sprintf(IA6_IN6(rap->ra_addr))); return EADDRINUSE /* XXX */; } ia->ia6_ifpr = ifpr; return 0; } - error = assigne_ra_entry(ifpr2rp(ifpr), iilen, ia); + error = assign_ra_entry(ifpr2rp(ifpr), iilen, ia); if (error == 0) ia->ia6_ifpr = ifpr; return (error); } void in6_prefix_remove_ifid(int iilen, struct in6_ifaddr *ia) { struct rp_addr *rap; if (ia->ia6_ifpr == NULL) return; rap = search_ifidwithprefix(ifpr2rp(ia->ia6_ifpr), IA6_IN6(ia)); if (rap != NULL) { int s = splnet(); LIST_REMOVE(rap, ra_entry); splx(s); + if (rap->ra_addr) + IFAFREE(&rap->ra_addr->ia_ifa); free(rap, M_RR_ADDR); } + if (LIST_EMPTY(&ifpr2rp(ia->ia6_ifpr)->rp_addrhead)) rp_remove(ifpr2rp(ia->ia6_ifpr)); } +void +in6_purgeprefix(ifp) + struct ifnet *ifp; +{ + struct ifprefix *ifpr, *nextifpr; + + /* delete prefixes before ifnet goes away */ + for (ifpr = TAILQ_FIRST(&ifp->if_prefixhead); ifpr; + ifpr = nextifpr) + { + nextifpr = TAILQ_NEXT(ifpr, ifpr_list); + if (ifpr->ifpr_prefix->sa_family != AF_INET6 || + ifpr->ifpr_type != IN6_PREFIX_RR) + continue; + (void)delete_each_prefix(ifpr2rp(ifpr), PR_ORIG_KERNEL); + } +} + static void add_each_addr(struct socket *so, struct rr_prefix *rpp, struct rp_addr *rap) { struct in6_ifaddr *ia6; struct in6_aliasreq ifra; int error; /* init ifra */ bzero(&ifra, sizeof(ifra)); strncpy(ifra.ifra_name, if_name(rpp->rp_ifp), sizeof(ifra.ifra_name)); ifra.ifra_addr.sin6_family = ifra.ifra_prefixmask.sin6_family = AF_INET6; ifra.ifra_addr.sin6_len = ifra.ifra_prefixmask.sin6_len = sizeof(ifra.ifra_addr); /* copy prefix part */ bit_copy((char *)&ifra.ifra_addr.sin6_addr, sizeof(ifra.ifra_addr.sin6_addr) << 3, (char *)RP_IN6(rpp), sizeof(*RP_IN6(rpp)) << 3, 0, rpp->rp_plen); /* copy interface id part */ bit_copy((char *)&ifra.ifra_addr.sin6_addr, sizeof(ifra.ifra_addr.sin6_addr) << 3, (char *)&rap->ra_ifid, sizeof(rap->ra_ifid) << 3, rpp->rp_plen, (sizeof(rap->ra_ifid) << 3) - rpp->rp_plen); in6_prefixlen2mask(&ifra.ifra_prefixmask.sin6_addr, rpp->rp_plen); /* don't care ifra_flags for now */ ia6 = in6ifa_ifpwithaddr(rpp->rp_ifp, &ifra.ifra_addr.sin6_addr); if (ia6 != NULL) { if (ia6->ia6_ifpr == NULL) { /* link this addr and the prefix each other */ + IFAFREE(&rap->ra_addr->ia_ifa); rap->ra_addr = ia6; + rap->ra_addr->ia_ifa.ifa_refcnt++; ia6->ia6_ifpr = rp2ifpr(rpp); return; } if (ia6->ia6_ifpr == rp2ifpr(rpp)) { + IFAFREE(&rap->ra_addr->ia_ifa); rap->ra_addr = ia6; + rap->ra_addr->ia_ifa.ifa_refcnt++; return; } /* * The addr is already assigned to other * prefix. * There may be some inconsistencies between * prefixes. * e.g. overraped prefixes with common starting * part and different plefixlen. * Or, completely duplicated prefixes? * log it and return. */ log(LOG_ERR, "in6_prefix.c: add_each_addr: addition of an addr" - "%s/%d failed because there is already another addr %s/%d", + "%s/%d failed because there is already another addr %s/%d\n", ip6_sprintf(&ifra.ifra_addr.sin6_addr), rpp->rp_plen, ip6_sprintf(IA6_IN6(ia6)), in6_mask2len(&ia6->ia_prefixmask.sin6_addr)); return; } /* propagate ANYCAST flag if it is set for ancestor addr */ if (rap->ra_flags.anycast != 0) ifra.ifra_flags |= IN6_IFF_ANYCAST; error = in6_control(so, SIOCAIFADDR_IN6, (caddr_t)&ifra, rpp->rp_ifp, - curproc); + curproc); if (error != 0) log(LOG_ERR, "in6_prefix.c: add_each_addr: addition of an addr" - "%s/%d failed because in6_control failed for error %d", + "%s/%d failed because in6_control failed for error %d\n", ip6_sprintf(&ifra.ifra_addr.sin6_addr), rpp->rp_plen, error); return; /* * link beween this addr and the prefix will be done * in in6_prefix_add_ifid */ } static int rrpr_update(struct socket *so, struct rr_prefix *new) { struct rr_prefix *rpp; struct ifprefix *ifpr; struct rp_addr *rap; int s; /* search existing prefix */ for (ifpr = TAILQ_FIRST(&new->rp_ifp->if_prefixhead); ifpr; - ifpr = TAILQ_NEXT(ifpr, ifpr_list)) { + ifpr = TAILQ_NEXT(ifpr, ifpr_list)) + { if (ifpr->ifpr_prefix->sa_family != AF_INET6 || ifpr->ifpr_type != IN6_PREFIX_RR) continue; if (ifpr->ifpr_plen == new->rp_plen && in6_are_prefix_equal(IFPR_IN6(ifpr), RP_IN6(new), ifpr->ifpr_plen)) break; } rpp = ifpr2rp(ifpr); if (rpp != NULL) { /* * We got a prefix which we have seen in the past. */ /* * If the origin of the already-installed prefix is more * preferable than the new one, ignore installation request. */ if (rpp->rp_origin > new->rp_origin) return(EPERM); /* update prefix information */ rpp->rp_flags.prf_ra = new->rp_flags.prf_ra; if (rpp->rp_origin >= PR_ORIG_RR) rpp->rp_flags.prf_rr = new->rp_flags.prf_rr; rpp->rp_vltime = new->rp_vltime; rpp->rp_pltime = new->rp_pltime; rpp->rp_expire = new->rp_expire; rpp->rp_preferred = new->rp_preferred; rpp->rp_statef_delmark = 0; /* cancel deletion */ /* * Interface id related update. * add rp_addr entries in new into rpp, if they have not * been already included in rpp. */ while (!LIST_EMPTY(&new->rp_addrhead)) { rap = LIST_FIRST(&new->rp_addrhead); LIST_REMOVE(rap, ra_entry); if (search_ifidwithprefix(rpp, &rap->ra_ifid) != NULL) { + if (rap->ra_addr) + IFAFREE(&rap->ra_addr->ia_ifa); free(rap, M_RR_ADDR); continue; } s = splnet(); LIST_INSERT_HEAD(&rpp->rp_addrhead, rap, ra_entry); splx(s); } } else { /* * We got a fresh prefix. */ /* create new prefix */ rpp = (struct rr_prefix *)malloc(sizeof(*rpp), M_IP6RR, M_NOWAIT); if (rpp == NULL) { log(LOG_ERR, "in6_prefix.c: rrpr_update:%d" - ": ENOBUFS for rr_prefix", __LINE__); + ": ENOBUFS for rr_prefix\n", __LINE__); return(ENOBUFS); } /* initilization */ *rpp = *new; LIST_INIT(&rpp->rp_addrhead); /* move rp_addr entries of new to rpp */ while (!LIST_EMPTY(&new->rp_addrhead)) { rap = LIST_FIRST(&new->rp_addrhead); LIST_REMOVE(rap, ra_entry); LIST_INSERT_HEAD(&rpp->rp_addrhead, rap, ra_entry); } /* let rp_ifpr.ifpr_prefix point rr_prefix. */ rpp->rp_ifpr.ifpr_prefix = (struct sockaddr *)&rpp->rp_prefix; - /* link rr_prefix entry to if_prefixhead */ + /* link rr_prefix entry to if_prefixlist */ { struct ifnet *ifp = rpp->rp_ifp; struct ifprefix *ifpr; if ((ifpr = TAILQ_FIRST(&ifp->if_prefixhead)) != NULL) { for ( ; TAILQ_NEXT(ifpr, ifpr_list); ifpr = TAILQ_NEXT(ifpr, ifpr_list)) continue; TAILQ_NEXT(ifpr, ifpr_list) = rp2ifpr(rpp); } else TAILQ_FIRST(&ifp->if_prefixhead) = rp2ifpr(rpp); rp2ifpr(rpp)->ifpr_type = IN6_PREFIX_RR; } /* link rr_prefix entry to rr_prefix list */ s = splnet(); LIST_INSERT_HEAD(&rr_prefix, rpp, rp_entry); splx(s); } if (!new->rp_raf_auto) return 0; /* * Add an address for each interface id, if it is not yet * If it existed but not pointing to the prefix yet, * init the prefix pointer. */ - LIST_FOREACH(rap, &rpp->rp_addrhead, ra_entry) { + LIST_FOREACH(rap, &rpp->rp_addrhead, ra_entry) + { if (rap->ra_addr != NULL) { if (rap->ra_addr->ia6_ifpr == NULL) rap->ra_addr->ia6_ifpr = rp2ifpr(rpp); continue; } add_each_addr(so, rpp, rap); } return 0; } static int add_each_prefix(struct socket *so, struct rr_prefix *rpp) { init_prefix_ltimes(rpp); return(rrpr_update(so, rpp)); } static void rp_remove(struct rr_prefix *rpp) { int s; s = splnet(); - /* unlink rp_entry from if_prefixhead */ + /* unlink rp_entry from if_prefixlist */ { struct ifnet *ifp = rpp->rp_ifp; struct ifprefix *ifpr; if ((ifpr = TAILQ_FIRST(&ifp->if_prefixhead)) == rp2ifpr(rpp)) TAILQ_FIRST(&ifp->if_prefixhead) = TAILQ_NEXT(ifpr, ifpr_list); else { while (TAILQ_NEXT(ifpr, ifpr_list) != NULL && (TAILQ_NEXT(ifpr, ifpr_list) != rp2ifpr(rpp))) ifpr = TAILQ_NEXT(ifpr, ifpr_list); if (TAILQ_NEXT(ifpr, ifpr_list)) TAILQ_NEXT(ifpr, ifpr_list) = TAILQ_NEXT(rp2ifpr(rpp), ifpr_list); - else - printf("Couldn't unlink rr_prefix from ifp\n"); + else + printf("Couldn't unlink rr_prefix from ifp\n"); } } /* unlink rp_entry from rr_prefix list */ LIST_REMOVE(rpp, rp_entry); splx(s); free(rpp, M_IP6RR); } static int create_ra_entry(struct rp_addr **rapp) { *rapp = (struct rp_addr *)malloc(sizeof(struct rp_addr), M_RR_ADDR, M_NOWAIT); if (*rapp == NULL) { log(LOG_ERR, "in6_prefix.c: init_newprefix:%d: ENOBUFS" - "for rp_addr", __LINE__); + "for rp_addr\n", __LINE__); return ENOBUFS; } bzero(*rapp, sizeof(*(*rapp))); return 0; } static int init_newprefix(struct in6_rrenumreq *irr, struct ifprefix *ifpr, struct rr_prefix *rpp) { struct rp_addr *orap; /* init rp */ bzero(rpp, sizeof(*rpp)); rpp->rp_type = IN6_PREFIX_RR; rpp->rp_ifp = ifpr->ifpr_ifp; rpp->rp_plen = ifpr->ifpr_plen; rpp->rp_prefix.sin6_len = sizeof(rpp->rp_prefix); rpp->rp_prefix.sin6_family = AF_INET6; bit_copy((char *)RP_IN6(rpp), sizeof(*RP_IN6(rpp)) << 3, (char *)&irr->irr_useprefix.sin6_addr, sizeof(irr->irr_useprefix.sin6_addr) << 3, 0, irr->irr_u_uselen); /* copy keeplen part if necessary as necessary len */ if (irr->irr_u_uselen < ifpr->ifpr_plen) bit_copy((char *)RP_IN6(rpp), sizeof(*RP_IN6(rpp)) << 3, (char *)IFPR_IN6(ifpr), sizeof(*IFPR_IN6(ifpr)) << 3, irr->irr_u_uselen, min(ifpr->ifpr_plen - irr->irr_u_uselen, irr->irr_u_keeplen)); - LIST_FOREACH(orap, &(ifpr2rp(ifpr)->rp_addrhead), ra_entry) { + LIST_FOREACH(orap, &(ifpr2rp(ifpr)->rp_addrhead), ra_entry) + { struct rp_addr *rap; int error = 0; if ((error = create_ra_entry(&rap)) != 0) return error; rap->ra_ifid = orap->ra_ifid; rap->ra_flags.anycast = (orap->ra_addr != NULL && (orap->ra_addr->ia6_flags & IN6_IFF_ANYCAST) != 0) ? 1 : 0; LIST_INSERT_HEAD(&rpp->rp_addrhead, rap, ra_entry); } rpp->rp_vltime = irr->irr_vltime; rpp->rp_pltime = irr->irr_pltime; rpp->rp_raf_onlink = irr->irr_raf_mask_onlink ? irr->irr_raf_onlink : ifpr2rp(ifpr)->rp_raf_onlink; rpp->rp_raf_auto = irr->irr_raf_mask_auto ? irr->irr_raf_auto : ifpr2rp(ifpr)->rp_raf_auto; /* Is some FlagMasks for rrf necessary? */ rpp->rp_rrf = irr->irr_rrf; rpp->rp_origin = irr->irr_origin; return 0; } static void free_rp_entries(struct rr_prefix *rpp) { /* * This func is only called with rpp on stack(not on list). * So no splnet() here */ while (!LIST_EMPTY(&rpp->rp_addrhead)) { struct rp_addr *rap; rap = LIST_FIRST(&rpp->rp_addrhead); LIST_REMOVE(rap, ra_entry); + if (rap->ra_addr) + IFAFREE(&rap->ra_addr->ia_ifa); free(rap, M_RR_ADDR); } } static int add_useprefixes(struct socket *so, struct ifnet *ifp, struct in6_rrenumreq *irr) { struct ifprefix *ifpr, *nextifpr; struct rr_prefix rp; int error = 0; /* add prefixes to each of marked prefix */ - for (ifpr = TAILQ_FIRST(&ifp->if_prefixhead); ifpr; ifpr = nextifpr) { + for (ifpr = TAILQ_FIRST(&ifp->if_prefixhead); ifpr; ifpr = nextifpr) + { nextifpr = TAILQ_NEXT(ifpr, ifpr_list); if (ifpr->ifpr_prefix->sa_family != AF_INET6 || ifpr->ifpr_type != IN6_PREFIX_RR) continue; if (ifpr2rp(ifpr)->rp_statef_addmark) { if ((error = init_newprefix(irr, ifpr, &rp)) != 0) break; error = add_each_prefix(so, &rp); } } /* free each rp_addr entry */ free_rp_entries(&rp); return error; } static void unprefer_prefix(struct rr_prefix *rpp) { struct rp_addr *rap; - LIST_FOREACH(rap, &rpp->rp_addrhead, ra_entry) { + for (rap = rpp->rp_addrhead.lh_first; rap != NULL; + rap = rap->ra_entry.le_next) { if (rap->ra_addr == NULL) continue; rap->ra_addr->ia6_lifetime.ia6t_preferred = time_second; rap->ra_addr->ia6_lifetime.ia6t_pltime = 0; } } int -delete_each_prefix(struct socket *so, struct rr_prefix *rpp, u_char origin) +delete_each_prefix(struct rr_prefix *rpp, u_char origin) { - struct in6_aliasreq ifra; int error = 0; if (rpp->rp_origin > origin) return(EPERM); - while (!LIST_EMPTY(&rpp->rp_addrhead)) { + while (rpp->rp_addrhead.lh_first != NULL) { struct rp_addr *rap; int s; s = splnet(); rap = LIST_FIRST(&rpp->rp_addrhead); if (rap == NULL) break; LIST_REMOVE(rap, ra_entry); splx(s); if (rap->ra_addr == NULL) { free(rap, M_RR_ADDR); continue; } rap->ra_addr->ia6_ifpr = NULL; - bzero(&ifra, sizeof(ifra)); - strncpy(ifra.ifra_name, if_name(rpp->rp_ifp), - sizeof(ifra.ifra_name)); - ifra.ifra_addr = rap->ra_addr->ia_addr; - ifra.ifra_dstaddr = rap->ra_addr->ia_dstaddr; - ifra.ifra_prefixmask = rap->ra_addr->ia_prefixmask; - - error = in6_control(so, SIOCDIFADDR_IN6, (caddr_t)&ifra, - rpp->rp_ifp, curproc); - if (error != 0) - log(LOG_ERR, "in6_prefix.c: delete_each_prefix:" - "deletion of an addr %s/%d failed because" - "in6_control failed for error %d", - ip6_sprintf(&ifra.ifra_addr.sin6_addr), - rpp->rp_plen, error); - + in6_purgeaddr(&rap->ra_addr->ia_ifa, rpp->rp_ifp); + IFAFREE(&rap->ra_addr->ia_ifa); free(rap, M_RR_ADDR); } rp_remove(rpp); return error; } static void -delete_prefixes(struct socket *so, struct ifnet *ifp, u_char origin) +delete_prefixes(struct ifnet *ifp, u_char origin) { struct ifprefix *ifpr, *nextifpr; /* delete prefixes marked as tobe deleted */ - for (ifpr = TAILQ_FIRST(&ifp->if_prefixhead); ifpr; ifpr = nextifpr) { + for (ifpr = TAILQ_FIRST(&ifp->if_prefixhead); ifpr; ifpr = nextifpr) + { nextifpr = TAILQ_NEXT(ifpr, ifpr_list); if (ifpr->ifpr_prefix->sa_family != AF_INET6 || ifpr->ifpr_type != IN6_PREFIX_RR) continue; if (ifpr2rp(ifpr)->rp_statef_delmark) - (void)delete_each_prefix(so, ifpr2rp(ifpr), origin); + (void)delete_each_prefix(ifpr2rp(ifpr), origin); } } static int link_stray_ia6s(struct rr_prefix *rpp) { struct ifaddr *ifa; - TAILQ_FOREACH(ifa, &rpp->rp_ifp->if_addrlist, ifa_list) + for (ifa = rpp->rp_ifp->if_addrlist.tqh_first; ifa; + ifa = ifa->ifa_list.tqe_next) { struct rp_addr *rap; struct rr_prefix *orpp; int error = 0; if (ifa->ifa_addr->sa_family != AF_INET6) continue; if (rpp->rp_plen > in6_matchlen(RP_IN6(rpp), IFA_IN6(ifa))) continue; orpp = ifpr2rp(((struct in6_ifaddr *)ifa)->ia6_ifpr); if (orpp != NULL) { if (!in6_are_prefix_equal(RP_IN6(orpp), RP_IN6(rpp), rpp->rp_plen)) log(LOG_ERR, "in6_prefix.c: link_stray_ia6s:" "addr %s/%d already linked to a prefix" - "and it matches also %s/%d", + "and it matches also %s/%d\n", ip6_sprintf(IFA_IN6(ifa)), orpp->rp_plen, ip6_sprintf(RP_IN6(rpp)), rpp->rp_plen); continue; } - if ((error = assigne_ra_entry(rpp, + if ((error = assign_ra_entry(rpp, (sizeof(rap->ra_ifid) << 3) - rpp->rp_plen, (struct in6_ifaddr *)ifa)) != 0) return error; } return 0; } +/* XXX assumes that permission is already checked by the caller */ int in6_prefix_ioctl(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp) { struct rr_prefix *rpp, rp_tmp; struct rp_addr *rap; struct in6_prefixreq *ipr = (struct in6_prefixreq *)data; struct in6_rrenumreq *irr = (struct in6_rrenumreq *)data; struct ifaddr *ifa; int error = 0; /* * Failsafe for errneous address config program. * Let's hope rrenumd don't make a mistakes. */ if (ipr->ipr_origin <= PR_ORIG_RA) ipr->ipr_origin = PR_ORIG_STATIC; switch (cmd) { case SIOCSGIFPREFIX_IN6: delmark_global_prefixes(ifp, irr); /* FALL THROUGH */ case SIOCAIFPREFIX_IN6: case SIOCCIFPREFIX_IN6: /* check if preferred lifetime > valid lifetime */ if (irr->irr_pltime > irr->irr_vltime) { log(LOG_NOTICE, "in6_prefix_ioctl: preferred lifetime" - "(%ld) is greater than valid lifetime(%ld)", + "(%ld) is greater than valid lifetime(%ld)\n", (u_long)irr->irr_pltime, (u_long)irr->irr_vltime); error = EINVAL; break; } if (mark_matched_prefixes(cmd, ifp, irr)) { if (irr->irr_u_uselen != 0) if ((error = add_useprefixes(so, ifp, irr)) != 0) goto failed; if (cmd != SIOCAIFPREFIX_IN6) - delete_prefixes(so, ifp, irr->irr_origin); + delete_prefixes(ifp, irr->irr_origin); } else return (EADDRNOTAVAIL); failed: unmark_prefixes(ifp); break; case SIOCGIFPREFIX_IN6: rpp = search_matched_prefix(ifp, ipr); if (rpp == NULL || ifp != rpp->rp_ifp) return (EADDRNOTAVAIL); ipr->ipr_origin = rpp->rp_origin; ipr->ipr_plen = rpp->rp_plen; ipr->ipr_vltime = rpp->rp_vltime; ipr->ipr_pltime = rpp->rp_pltime; ipr->ipr_flags = rpp->rp_flags; ipr->ipr_prefix = rpp->rp_prefix; break; case SIOCSIFPREFIX_IN6: /* check if preferred lifetime > valid lifetime */ if (ipr->ipr_pltime > ipr->ipr_vltime) { log(LOG_NOTICE, "in6_prefix_ioctl: preferred lifetime" - "(%ld) is greater than valid lifetime(%ld)", + "(%ld) is greater than valid lifetime(%ld)\n", (u_long)ipr->ipr_pltime, (u_long)ipr->ipr_vltime); error = EINVAL; break; } /* init rp_tmp */ bzero((caddr_t)&rp_tmp, sizeof(rp_tmp)); rp_tmp.rp_ifp = ifp; rp_tmp.rp_plen = ipr->ipr_plen; rp_tmp.rp_prefix = ipr->ipr_prefix; rp_tmp.rp_vltime = ipr->ipr_vltime; rp_tmp.rp_pltime = ipr->ipr_pltime; rp_tmp.rp_flags = ipr->ipr_flags; rp_tmp.rp_origin = ipr->ipr_origin; /* create rp_addr entries, usually at least for lladdr */ if ((error = link_stray_ia6s(&rp_tmp)) != 0) { free_rp_entries(&rp_tmp); break; } - TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) { + for (ifa = ifp->if_addrlist.tqh_first; + ifa; + ifa = ifa->ifa_list.tqe_next) + { if (ifa->ifa_addr == NULL) continue; /* just for safety */ if (ifa->ifa_addr->sa_family != AF_INET6) continue; if (IN6_IS_ADDR_LINKLOCAL(IFA_IN6(ifa)) == 0) continue; if ((error = create_ra_entry(&rap)) != 0) { free_rp_entries(&rp_tmp); goto bad; } /* copy interface id part */ bit_copy((caddr_t)&rap->ra_ifid, sizeof(rap->ra_ifid) << 3, (caddr_t)IFA_IN6(ifa), sizeof(*IFA_IN6(ifa)) << 3, rp_tmp.rp_plen, (sizeof(rap->ra_ifid) << 3) - rp_tmp.rp_plen); /* insert into list */ LIST_INSERT_HEAD(&rp_tmp.rp_addrhead, rap, ra_entry); } error = add_each_prefix(so, &rp_tmp); /* free each rp_addr entry */ free_rp_entries(&rp_tmp); break; case SIOCDIFPREFIX_IN6: rpp = search_matched_prefix(ifp, ipr); if (rpp == NULL || ifp != rpp->rp_ifp) return (EADDRNOTAVAIL); - error = delete_each_prefix(so, rpp, ipr->ipr_origin); + error = delete_each_prefix(rpp, ipr->ipr_origin); break; } bad: return error; } void in6_rr_timer(void *ignored_arg) { int s; struct rr_prefix *rpp; timeout(in6_rr_timer, (caddr_t)0, ip6_rr_prune * hz); s = splnet(); /* expire */ rpp = LIST_FIRST(&rr_prefix); while (rpp) { if (rpp->rp_expire && rpp->rp_expire < time_second) { struct rr_prefix *next_rpp; - struct socket so; - /* XXX: init dummy so */ - bzero(&so, sizeof(so)); - next_rpp = LIST_NEXT(rpp, rp_entry); - delete_each_prefix(&so, rpp, PR_ORIG_KERNEL); + delete_each_prefix(rpp, PR_ORIG_KERNEL); rpp = next_rpp; continue; } if (rpp->rp_preferred && rpp->rp_preferred < time_second) unprefer_prefix(rpp); rpp = LIST_NEXT(rpp, rp_entry); } splx(s); } Index: head/sys/netinet6/in6_prefix.h =================================================================== --- head/sys/netinet6/in6_prefix.h (revision 62586) +++ head/sys/netinet6/in6_prefix.h (revision 62587) @@ -1,88 +1,88 @@ +/* $FreeBSD$ */ +/* $KAME: in6_prefix.h,v 1.6 2000/03/25 07:23:45 sumikawa Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, 1998 and 1999 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ struct rr_prefix { - struct ifprefix rp_ifpr; + struct ifprefix rp_ifpr; LIST_ENTRY(rr_prefix) rp_entry; LIST_HEAD(rp_addrhead, rp_addr) rp_addrhead; - struct sockaddr_in6 rp_prefix; /* prefix */ - u_int32_t rp_vltime; /* advertised valid lifetime */ - u_int32_t rp_pltime; /* advertised preferred lifetime */ - time_t rp_expire; /* expiration time of the prefix */ - time_t rp_preferred; /* preferred time of the prefix */ - struct in6_prflags rp_flags; + struct sockaddr_in6 rp_prefix; /* prefix */ + u_int32_t rp_vltime; /* advertised valid lifetime */ + u_int32_t rp_pltime; /* advertised preferred lifetime */ + time_t rp_expire; /* expiration time of the prefix */ + time_t rp_preferred; /* preferred time of the prefix */ + struct in6_prflags rp_flags; u_char rp_origin; /* from where this prefix info is obtained */ struct rp_stateflags { /* if some prefix should be added to this prefix */ - u_char addmark : 1; - u_char delmark : 1; /* if this prefix will be deleted */ + u_char addmark : 1; + u_char delmark : 1; /* if this prefix will be deleted */ } rp_stateflags; }; -#define rp_type rp_ifpr.ifpr_type -#define rp_ifp rp_ifpr.ifpr_ifp -#define rp_plen rp_ifpr.ifpr_plen +#define rp_type rp_ifpr.ifpr_type +#define rp_ifp rp_ifpr.ifpr_ifp +#define rp_plen rp_ifpr.ifpr_plen -#define rp_raf rp_flags.prf_ra -#define rp_raf_onlink rp_flags.prf_ra.onlink -#define rp_raf_auto rp_flags.prf_ra.autonomous +#define rp_raf rp_flags.prf_ra +#define rp_raf_onlink rp_flags.prf_ra.onlink +#define rp_raf_auto rp_flags.prf_ra.autonomous -#define rp_statef_addmark rp_stateflags.addmark -#define rp_statef_delmark rp_stateflags.delmark +#define rp_statef_addmark rp_stateflags.addmark +#define rp_statef_delmark rp_stateflags.delmark -#define rp_rrf rp_flags.prf_rr -#define rp_rrf_decrvalid rp_flags.prf_rr.decrvalid -#define rp_rrf_decrprefd rp_flags.prf_rr.decrprefd +#define rp_rrf rp_flags.prf_rr +#define rp_rrf_decrvalid rp_flags.prf_rr.decrvalid +#define rp_rrf_decrprefd rp_flags.prf_rr.decrprefd struct rp_addr { - LIST_ENTRY(rp_addr) ra_entry; - struct in6_addr ra_ifid; - struct in6_ifaddr *ra_addr; - struct ra_flags { - u_char anycast : 1; + LIST_ENTRY(rp_addr) ra_entry; + struct in6_addr ra_ifid; + struct in6_ifaddr *ra_addr; + struct ra_flags { + u_char anycast : 1; } ra_flags; }; -#define ifpr2rp(ifpr) ((struct rr_prefix *)(ifpr)) -#define rp2ifpr(rp) ((struct ifprefix *)(rp)) +#define ifpr2rp(ifpr) ((struct rr_prefix *)(ifpr)) +#define rp2ifpr(rp) ((struct ifprefix *)(rp)) -#define RP_IN6(rp) (&(rp)->rp_prefix.sin6_addr) +#define RP_IN6(rp) (&(rp)->rp_prefix.sin6_addr) -#define RR_INFINITE_LIFETIME 0xffffffff +#define RR_INFINITE_LIFETIME 0xffffffff LIST_HEAD(rr_prhead, rr_prefix); -extern struct rr_prhead rr_prefix; +extern struct rr_prhead rr_prefix; -void in6_rr_timer __P((void *)); -int delete_each_prefix __P((struct socket *so, struct rr_prefix *rpp, - u_char origin)); +void in6_rr_timer __P((void *)); +int delete_each_prefix __P((struct rr_prefix *rpp, u_char origin)); Index: head/sys/netinet6/in6_proto.c =================================================================== --- head/sys/netinet6/in6_proto.c (revision 62586) +++ head/sys/netinet6/in6_proto.c (revision 62587) @@ -1,425 +1,560 @@ +/* $FreeBSD$ */ +/* $KAME: in6_proto.c,v 1.64 2000/06/20 16:20:27 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* * Copyright (c) 1982, 1986, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)in_proto.c 8.1 (Berkeley) 6/10/93 */ #include "opt_inet.h" +#include "opt_inet6.h" #include "opt_ipsec.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include #include #include -#include +#include #include -#include +#include #include #include #include #include #include #include + #include #include #include #include #ifdef IPSEC #include -#include #include +#include #include #ifdef IPSEC_ESP #include #include #endif +#include +#include #endif /*IPSEC*/ #include #include "gif.h" #if NGIF > 0 #include #endif #include #define offsetof(type, member) ((size_t)(&((type *)0)->member)) /* * TCP/IP protocol family: IP6, ICMP6, UDP, TCP. */ -extern struct domain inet6domain; -static struct pr_usrreqs nousrreqs; +extern struct domain inet6domain; +static struct pr_usrreqs nousrreqs; struct ip6protosw inet6sw[] = { { 0, &inet6domain, IPPROTO_IPV6, 0, 0, 0, 0, 0, 0, ip6_init, 0, frag6_slowtimo, frag6_drain, &nousrreqs, }, { SOCK_DGRAM, &inet6domain, IPPROTO_UDP, PR_ATOMIC | PR_ADDR, udp6_input, 0, udp6_ctlinput, ip6_ctloutput, 0, 0, 0, 0, 0, &udp6_usrreqs, }, { SOCK_STREAM, &inet6domain, IPPROTO_TCP, PR_CONNREQUIRED | PR_WANTRCVD, tcp6_input, 0, tcp6_ctlinput, tcp_ctloutput, 0, #ifdef INET /* don't call timeout routines twice */ tcp_init, 0, 0, tcp_drain, #else - tcp_init, 0, tcp_slowtimo, tcp_drain, + tcp_init, tcp_fasttimo, tcp_slowtimo, tcp_drain, #endif &tcp6_usrreqs, }, { SOCK_RAW, &inet6domain, IPPROTO_RAW, PR_ATOMIC | PR_ADDR, - rip6_input, rip6_output, 0, rip6_ctloutput, + rip6_input, rip6_output, rip6_ctlinput, rip6_ctloutput, 0, 0, 0, 0, 0, &rip6_usrreqs }, { SOCK_RAW, &inet6domain, IPPROTO_ICMPV6, PR_ATOMIC | PR_ADDR, icmp6_input, rip6_output, 0, rip6_ctloutput, 0, icmp6_init, icmp6_fasttimo, 0, 0, &rip6_usrreqs }, { SOCK_RAW, &inet6domain, IPPROTO_DSTOPTS,PR_ATOMIC|PR_ADDR, dest6_input, 0, 0, 0, - 0, + 0, 0, 0, 0, 0, &nousrreqs }, { SOCK_RAW, &inet6domain, IPPROTO_ROUTING,PR_ATOMIC|PR_ADDR, route6_input, 0, 0, 0, - 0, + 0, 0, 0, 0, 0, &nousrreqs }, { SOCK_RAW, &inet6domain, IPPROTO_FRAGMENT,PR_ATOMIC|PR_ADDR, frag6_input, 0, 0, 0, - 0, + 0, 0, 0, 0, 0, &nousrreqs }, #ifdef IPSEC { SOCK_RAW, &inet6domain, IPPROTO_AH, PR_ATOMIC|PR_ADDR, ah6_input, 0, 0, 0, - 0, + 0, 0, 0, 0, 0, &nousrreqs, }, #ifdef IPSEC_ESP { SOCK_RAW, &inet6domain, IPPROTO_ESP, PR_ATOMIC|PR_ADDR, esp6_input, 0, 0, 0, - 0, + 0, 0, 0, 0, 0, &nousrreqs, }, #endif +{ SOCK_RAW, &inet6domain, IPPROTO_IPCOMP, PR_ATOMIC|PR_ADDR, + ipcomp6_input, 0, 0, 0, + 0, + 0, 0, 0, 0, + &nousrreqs, +}, #endif /* IPSEC */ -#if NGIF > 0 +#ifdef INET { SOCK_RAW, &inet6domain, IPPROTO_IPV4, PR_ATOMIC|PR_ADDR, - in6_gif_input,0, 0, 0, - 0, + encap6_input, rip6_output, 0, rip6_ctloutput, + 0, 0, 0, 0, 0, - &nousrreqs + &rip6_usrreqs }, +#endif /*INET*/ { SOCK_RAW, &inet6domain, IPPROTO_IPV6, PR_ATOMIC|PR_ADDR, - in6_gif_input,0, 0, 0, + encap6_input, rip6_output, 0, rip6_ctloutput, 0, +#ifndef INET + encap_init, 0, 0, 0, +#else 0, 0, 0, 0, - &nousrreqs +#endif + &rip6_usrreqs }, -#endif /* GIF */ { SOCK_RAW, &inet6domain, IPPROTO_PIM, PR_ATOMIC|PR_ADDR, - pim6_input, rip6_output, 0, rip6_ctloutput, + pim6_input, rip6_output, 0, rip6_ctloutput, 0, - 0, 0, 0, 0, + 0, 0, 0, 0, &rip6_usrreqs }, /* raw wildcard */ { SOCK_RAW, &inet6domain, 0, PR_ATOMIC | PR_ADDR, rip6_input, rip6_output, 0, rip6_ctloutput, - 0, - 0, 0, 0, 0, + 0, 0, + 0, 0, 0, &rip6_usrreqs }, }; -extern int in6_inithead __P((void **, int)); +#if NGIF > 0 +struct ip6protosw in6_gif_protosw = +{ SOCK_RAW, &inet6domain, 0/*IPPROTO_IPV[46]*/, PR_ATOMIC|PR_ADDR, + in6_gif_input, rip6_output, 0, rip6_ctloutput, + 0, + 0, 0, 0, 0, + &rip6_usrreqs +}; +#endif /*NGIF*/ +extern int in6_inithead __P((void **, int)); + struct domain inet6domain = { AF_INET6, "internet6", 0, 0, 0, (struct protosw *)inet6sw, (struct protosw *)&inet6sw[sizeof(inet6sw)/sizeof(inet6sw[0])], 0, in6_inithead, offsetof(struct sockaddr_in6, sin6_addr) << 3, sizeof(struct sockaddr_in6) }; DOMAIN_SET(inet6); /* * Internet configuration info */ #ifndef IPV6FORWARDING #ifdef GATEWAY6 #define IPV6FORWARDING 1 /* forward IP6 packets not for us */ #else #define IPV6FORWARDING 0 /* don't forward IP6 packets not for us */ #endif /* GATEWAY6 */ #endif /* !IPV6FORWARDING */ #ifndef IPV6_SENDREDIRECTS #define IPV6_SENDREDIRECTS 1 #endif int ip6_forwarding = IPV6FORWARDING; /* act as router? */ int ip6_sendredirects = IPV6_SENDREDIRECTS; int ip6_defhlim = IPV6_DEFHLIM; int ip6_defmcasthlim = IPV6_DEFAULT_MULTICAST_HOPS; int ip6_accept_rtadv = 0; /* "IPV6FORWARDING ? 0 : 1" is dangerous */ int ip6_maxfragpackets = 200; int ip6_log_interval = 5; int ip6_hdrnestlimit = 50; /* appropriate? */ int ip6_dad_count = 1; /* DupAddrDetectionTransmits */ -u_int32_t ip6_flow_seq; +u_int32_t ip6_flow_seq; int ip6_auto_flowlabel = 1; #if NGIF > 0 int ip6_gif_hlim = GIF_HLIM; #else int ip6_gif_hlim = 0; #endif int ip6_use_deprecated = 1; /* allow deprecated addr (RFC2462 5.5.4) */ int ip6_rr_prune = 5; /* router renumbering prefix * walk list every 5 sec. */ int ip6_mapped_addr_on = 1; u_int32_t ip6_id = 0UL; int ip6_keepfaith = 0; time_t ip6_log_time = (time_t)0L; /* icmp6 */ /* * BSDI4 defines these variables in in_proto.c... * XXX: what if we don't define INET? Should we define pmtu6_expire * or so? (jinmei@kame.net 19990310) */ -int pmtu_expire = 60*10; -int pmtu_probe = 60*2; +int pmtu_expire = 60*10; +int pmtu_probe = 60*2; /* raw IP6 parameters */ /* * Nominal space allocated to a raw ip socket. */ #define RIPV6SNDQ 8192 #define RIPV6RCVQ 8192 u_long rip6_sendspace = RIPV6SNDQ; u_long rip6_recvspace = RIPV6RCVQ; /* ICMPV6 parameters */ int icmp6_rediraccept = 1; /* accept and process redirects */ int icmp6_redirtimeout = 10 * 60; /* 10 minutes */ -u_int icmp6errratelim = 1000; /* 1000usec = 1msec */ +struct timeval icmp6errratelim = { 0, 0 }; /* no ratelimit */ +int icmp6errppslim = 100; /* 100pps */ +int icmp6_nodeinfo = 1; /* enable/disable NI response */ +#ifdef TCP6 +/* TCP on IP6 parameters */ +int tcp6_sendspace = 1024 * 8; +int tcp6_recvspace = 1024 * 8; +int tcp6_mssdflt = TCP6_MSS; +int tcp6_rttdflt = TCP6TV_SRTTDFLT / PR_SLOWHZ; +int tcp6_do_rfc1323 = 1; +int tcp6_conntimeo = TCP6TV_KEEP_INIT; /* initial connection timeout */ +int tcp6_43maxseg = 0; +int tcp6_pmtu = 0; + +/* + * Parameters for keepalive option. + * Connections for which SO_KEEPALIVE is set will be probed + * after being idle for a time of tcp6_keepidle (in units of PR_SLOWHZ). + * Starting at that time, the connection is probed at intervals + * of tcp6_keepintvl (same units) until a response is received + * or until tcp6_keepcnt probes have been made, at which time + * the connection is dropped. Note that a tcp6_keepidle value + * under 2 hours is nonconformant with RFC-1122, Internet Host Requirements. + */ +int tcp6_keepidle = TCP6TV_KEEP_IDLE; /* time before probing idle */ +int tcp6_keepintvl = TCP6TV_KEEPINTVL; /* interval betwn idle probes */ +int tcp6_keepcnt = TCP6TV_KEEPCNT; /* max idle probes */ +int tcp6_maxpersistidle = TCP6TV_KEEP_IDLE; /* max idle time in persist */ + +#ifndef INET_SERVER +#define TCP6_LISTEN_HASH_SIZE 17 +#define TCP6_CONN_HASH_SIZE 97 +#define TCP6_SYN_HASH_SIZE 293 +#define TCP6_SYN_BUCKET_SIZE 35 +#else +#define TCP6_LISTEN_HASH_SIZE 97 +#define TCP6_CONN_HASH_SIZE 9973 +#define TCP6_SYN_HASH_SIZE 997 +#define TCP6_SYN_BUCKET_SIZE 35 +#endif +int tcp6_listen_hash_size = TCP6_LISTEN_HASH_SIZE; +int tcp6_conn_hash_size = TCP6_CONN_HASH_SIZE; +struct tcp6_hash_list tcp6_listen_hash[TCP6_LISTEN_HASH_SIZE], + tcp6_conn_hash[TCP6_CONN_HASH_SIZE]; + +int tcp6_syn_cache_size = TCP6_SYN_HASH_SIZE; +int tcp6_syn_cache_limit = TCP6_SYN_HASH_SIZE*TCP6_SYN_BUCKET_SIZE; +int tcp6_syn_bucket_limit = 3*TCP6_SYN_BUCKET_SIZE; +struct syn_cache_head6 tcp6_syn_cache[TCP6_SYN_HASH_SIZE]; +struct syn_cache_head6 *tcp6_syn_cache_first; +int tcp6_syn_cache_interval = 8; /* runs timer every 4 seconds */ +int tcp6_syn_cache_timeo = TCP6TV_KEEP_INIT; + +/* + * Parameters for computing a desirable data segment size + * given an upper bound (either interface MTU, or peer's MSS option)_. + * As applications tend to use a buffer size that is a multiple + * of kilobytes, try for something that divides evenly. However, + * do not round down too much. + * + * Round segment size down to a multiple of TCP6_ROUNDSIZE if this + * does not result in lowering by more than (size/TCP6_ROUNDFRAC). + * For example, round 536 to 512. Older versions of the system + * effectively used MCLBYTES (1K or 2K) as TCP6_ROUNDSIZE, with + * a value of 1 for TCP6_ROUNDFRAC (eliminating its effect). + * We round to a multiple of 256 for SLIP. + */ +#ifndef TCP6_ROUNDSIZE +#define TCP6_ROUNDSIZE 256 /* round to multiple of 256 */ +#endif +#ifndef TCP6_ROUNDFRAC +#define TCP6_ROUNDFRAC 10 /* round down at most N/10, or 10% */ +#endif + +int tcp6_roundsize = TCP6_ROUNDSIZE; +int tcp6_roundfrac = TCP6_ROUNDFRAC; +#endif /*TCP6*/ + /* UDP on IP6 parameters */ int udp6_sendspace = 9216; /* really max datagram size */ int udp6_recvspace = 40 * (1024 + sizeof(struct sockaddr_in6)); /* 40 1K datagrams */ /* * sysctl related items. */ SYSCTL_NODE(_net, PF_INET6, inet6, CTLFLAG_RW, 0, "Internet6 Family"); /* net.inet6 */ SYSCTL_NODE(_net_inet6, IPPROTO_IPV6, ip6, CTLFLAG_RW, 0, "IP6"); SYSCTL_NODE(_net_inet6, IPPROTO_ICMPV6, icmp6, CTLFLAG_RW, 0, "ICMP6"); SYSCTL_NODE(_net_inet6, IPPROTO_UDP, udp6, CTLFLAG_RW, 0, "UDP6"); SYSCTL_NODE(_net_inet6, IPPROTO_TCP, tcp6, CTLFLAG_RW, 0, "TCP6"); #ifdef IPSEC SYSCTL_NODE(_net_inet6, IPPROTO_ESP, ipsec6, CTLFLAG_RW, 0, "IPSEC6"); #endif /* IPSEC */ /* net.inet6.ip6 */ static int sysctl_ip6_forwarding(SYSCTL_HANDLER_ARGS) { int error = 0; int old_ip6_forwarding; int changed; error = SYSCTL_OUT(req, arg1, sizeof(int)); if (error || !req->newptr) return (error); old_ip6_forwarding = ip6_forwarding; error = SYSCTL_IN(req, arg1, sizeof(int)); if (error != 0) return (error); changed = (ip6_forwarding ? 1 : 0) ^ (old_ip6_forwarding ? 1 : 0); if (changed == 0) return (error); + /* + * XXX while host->router removes prefix got from RA, + * router->host case nukes all the prefixes managed by in6_prefix.c + * (both RR and static). therefore, switching from host->router->host + * will remove statically configured addresses/prefixes. + * not sure if it is intended behavior or not. + */ if (ip6_forwarding != 0) { /* host becomes router */ int s = splnet(); struct nd_prefix *pr, *next; - for (pr = LIST_FIRST(&nd_prefix); pr; pr = next) { - next = LIST_NEXT(pr, ndpr_entry); + for (pr = nd_prefix.lh_first; pr; pr = next) { + next = pr->ndpr_next; if (!IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr)) in6_ifdel(pr->ndpr_ifp, &pr->ndpr_addr); prelist_remove(pr); } splx(s); } else { /* router becomes host */ - struct socket so; - - /* XXX: init dummy so */ - bzero(&so, sizeof(so)); while(!LIST_EMPTY(&rr_prefix)) - delete_each_prefix(&so, LIST_FIRST(&rr_prefix), + delete_each_prefix(LIST_FIRST(&rr_prefix), PR_ORIG_KERNEL); } return (error); } +static int +sysctl_icmp6_ratelimit (SYSCTL_HANDLER_ARGS) +{ + int rate_usec, error, s; + + /* + * The sysctl specifies the rate in usec-between-icmp, + * so we must convert from/to a timeval. + */ + rate_usec = (icmp6errratelim.tv_sec * 1000000) + + icmp6errratelim.tv_usec; + error = sysctl_handle_int(oidp, &rate_usec, 0, req); + if (error) + return (error); + if (rate_usec < 0) + return (EINVAL); + s = splnet(); + icmp6errratelim.tv_sec = rate_usec / 1000000; + icmp6errratelim.tv_usec = rate_usec % 1000000; + splx(s); + + return (0); +} + SYSCTL_OID(_net_inet6_ip6, IPV6CTL_FORWARDING, forwarding, CTLTYPE_INT|CTLFLAG_RW, &ip6_forwarding, 0, sysctl_ip6_forwarding, "I", ""); SYSCTL_INT(_net_inet6_ip6, IPV6CTL_SENDREDIRECTS, redirect, CTLFLAG_RW, &ip6_sendredirects, 0, ""); SYSCTL_INT(_net_inet6_ip6, IPV6CTL_DEFHLIM, hlim, CTLFLAG_RW, &ip6_defhlim, 0, ""); SYSCTL_INT(_net_inet6_ip6, IPV6CTL_MAXFRAGPACKETS, maxfragpackets, CTLFLAG_RW, &ip6_maxfragpackets, 0, ""); SYSCTL_INT(_net_inet6_ip6, IPV6CTL_ACCEPT_RTADV, accept_rtadv, CTLFLAG_RW, &ip6_accept_rtadv, 0, ""); SYSCTL_INT(_net_inet6_ip6, IPV6CTL_KEEPFAITH, keepfaith, CTLFLAG_RW, &ip6_keepfaith, 0, ""); SYSCTL_INT(_net_inet6_ip6, IPV6CTL_LOG_INTERVAL, log_interval, CTLFLAG_RW, &ip6_log_interval, 0, ""); SYSCTL_INT(_net_inet6_ip6, IPV6CTL_HDRNESTLIMIT, hdrnestlimit, CTLFLAG_RW, &ip6_hdrnestlimit, 0, ""); SYSCTL_INT(_net_inet6_ip6, IPV6CTL_DAD_COUNT, dad_count, CTLFLAG_RW, &ip6_dad_count, 0, ""); SYSCTL_INT(_net_inet6_ip6, IPV6CTL_AUTO_FLOWLABEL, auto_flowlabel, CTLFLAG_RW, &ip6_auto_flowlabel, 0, ""); SYSCTL_INT(_net_inet6_ip6, IPV6CTL_DEFMCASTHLIM, defmcasthlim, CTLFLAG_RW, &ip6_defmcasthlim, 0, ""); SYSCTL_INT(_net_inet6_ip6, IPV6CTL_GIF_HLIM, gifhlim, CTLFLAG_RW, &ip6_gif_hlim, 0, ""); SYSCTL_STRING(_net_inet6_ip6, IPV6CTL_KAME_VERSION, kame_version, CTLFLAG_RD, __KAME_VERSION, 0, ""); SYSCTL_INT(_net_inet6_ip6, IPV6CTL_USE_DEPRECATED, use_deprecated, CTLFLAG_RW, &ip6_use_deprecated, 0, ""); SYSCTL_INT(_net_inet6_ip6, IPV6CTL_RR_PRUNE, rr_prune, CTLFLAG_RW, &ip6_rr_prune, 0, ""); SYSCTL_INT(_net_inet6_ip6, IPV6CTL_MAPPED_ADDR, mapped_addr, CTLFLAG_RW, &ip6_mapped_addr_on, 0, ""); /* net.inet6.icmp6 */ SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_REDIRACCEPT, rediraccept, CTLFLAG_RW, &icmp6_rediraccept, 0, ""); SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_REDIRTIMEOUT, redirtimeout, CTLFLAG_RW, &icmp6_redirtimeout, 0, ""); SYSCTL_STRUCT(_net_inet6_icmp6, ICMPV6CTL_STATS, stats, CTLFLAG_RD, &icmp6stat, icmp6stat, ""); -SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_ERRRATELIMIT, - errratelimit, CTLFLAG_RW, &icmp6errratelim, 0, ""); +SYSCTL_PROC(_net_inet6_icmp6, ICMPV6CTL_ERRRATELIMIT, + errratelimit, CTLTYPE_INT|CTLFLAG_RW, + 0, sizeof(int), sysctl_icmp6_ratelimit, "I", ""); SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_ND6_PRUNE, nd6_prune, CTLFLAG_RW, &nd6_prune, 0, ""); SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_ND6_DELAY, nd6_delay, CTLFLAG_RW, &nd6_delay, 0, ""); SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_ND6_UMAXTRIES, nd6_umaxtries, CTLFLAG_RW, &nd6_umaxtries, 0, ""); SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_ND6_MMAXTRIES, nd6_mmaxtries, CTLFLAG_RW, &nd6_mmaxtries, 0, ""); SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_ND6_USELOOPBACK, nd6_useloopback, CTLFLAG_RW, &nd6_useloopback, 0, ""); -SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_ND6_PROXYALL, - nd6_proxyall, CTLFLAG_RW, &nd6_proxyall, 0, ""); +SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_NODEINFO, + nodeinfo, CTLFLAG_RW, &icmp6_nodeinfo, 0, ""); +SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_ERRPPSLIMIT, + errppslimit, CTLFLAG_RW, &icmp6errppslim, 0, ""); +SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_ND6_MAXNUDHINT, + nd6_maxnudhint, CTLFLAG_RW, &nd6_maxnudhint, 0, ""); Index: head/sys/netinet6/in6_rmx.c =================================================================== --- head/sys/netinet6/in6_rmx.c (revision 62586) +++ head/sys/netinet6/in6_rmx.c (revision 62587) @@ -1,477 +1,495 @@ +/* $FreeBSD$ */ +/* $KAME: in6_rmx.c,v 1.7 2000/04/06 08:30:43 sumikawa Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* * Copyright 1994, 1995 Massachusetts Institute of Technology * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby * granted, provided that both the above copyright notice and this * permission notice appear in all copies, that both the above * copyright notice and this permission notice appear in all * supporting documentation, and that the name of M.I.T. not be used * in advertising or publicity pertaining to distribution of the * software without specific, written prior permission. M.I.T. makes * no representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied * warranty. * * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: in6_rmx.c,v 1.3 1999/08/16 13:42:53 itojun Exp $ */ /* * This code does two things necessary for the enhanced TCP metrics to * function in a useful manner: * 1) It marks all non-host routes as `cloning', thus ensuring that * every actual reference to such a route actually gets turned * into a reference to a host route to the specific destination * requested. * 2) When such routes lose all their references, it arranges for them * to be deleted in some random collection of circumstances, so that * a large quantity of stale routing data is not kept in kernel memory * indefinitely. See in6_rtqtimo() below for the exact mechanism. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include -#include +#include #include #include #include #include extern int in6_inithead __P((void **head, int off)); -#define RTPRF_OURS RTF_PROTO3 /* set on routes we manage */ +#define RTPRF_OURS RTF_PROTO3 /* set on routes we manage */ /* * Do what we need to do when inserting a route. */ static struct radix_node * in6_addroute(void *v_arg, void *n_arg, struct radix_node_head *head, - struct radix_node *treenodes) + struct radix_node *treenodes) { struct rtentry *rt = (struct rtentry *)treenodes; struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)rt_key(rt); struct radix_node *ret; /* * For IPv6, all unicast non-host routes are automatically cloning. */ if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) rt->rt_flags |= RTF_MULTICAST; if (!(rt->rt_flags & (RTF_HOST | RTF_CLONING | RTF_MULTICAST))) { rt->rt_flags |= RTF_PRCLONING; } /* * A little bit of help for both IPv6 output and input: * For local addresses, we make sure that RTF_LOCAL is set, * with the thought that this might one day be used to speed up * ip_input(). * * We also mark routes to multicast addresses as such, because * it's easy to do and might be useful (but this is much more * dubious since it's so easy to inspect the address). (This * is done above.) * * XXX * should elaborate the code. */ if (rt->rt_flags & RTF_HOST) { if (IN6_ARE_ADDR_EQUAL(&satosin6(rt->rt_ifa->ifa_addr) ->sin6_addr, &sin6->sin6_addr)) { rt->rt_flags |= RTF_LOCAL; } } /* * We also specify a send and receive pipe size for every * route added, to help TCP a bit. TCP doesn't actually * want a true pipe size, which would be prohibitive in memory * costs and is hard to compute anyway; it simply uses these * values to size its buffers. So, we fill them in with the * same values that TCP would have used anyway, and allow the * installing program or the link layer to override these values * as it sees fit. This will hopefully allow TCP more * opportunities to save its ssthresh value. */ if (!rt->rt_rmx.rmx_sendpipe && !(rt->rt_rmx.rmx_locks & RTV_SPIPE)) rt->rt_rmx.rmx_sendpipe = tcp_sendspace; if (!rt->rt_rmx.rmx_recvpipe && !(rt->rt_rmx.rmx_locks & RTV_RPIPE)) rt->rt_rmx.rmx_recvpipe = tcp_recvspace; if (!rt->rt_rmx.rmx_mtu && !(rt->rt_rmx.rmx_locks & RTV_MTU) && rt->rt_ifp) rt->rt_rmx.rmx_mtu = rt->rt_ifp->if_mtu; ret = rn_addroute(v_arg, n_arg, head, treenodes); if (ret == NULL && rt->rt_flags & RTF_HOST) { struct rtentry *rt2; /* * We are trying to add a host route, but can't. * Find out if it is because of an * ARP entry and delete it if so. */ rt2 = rtalloc1((struct sockaddr *)sin6, 0, RTF_CLONING | RTF_PRCLONING); if (rt2) { if (rt2->rt_flags & RTF_LLINFO && rt2->rt_flags & RTF_HOST && rt2->rt_gateway && rt2->rt_gateway->sa_family == AF_LINK) { rtrequest(RTM_DELETE, (struct sockaddr *)rt_key(rt2), rt2->rt_gateway, rt_mask(rt2), rt2->rt_flags, 0); ret = rn_addroute(v_arg, n_arg, head, treenodes); } RTFREE(rt2); } } else if (ret == NULL && rt->rt_flags & RTF_CLONING) { struct rtentry *rt2; /* * We are trying to add a net route, but can't. * The following case should be allowed, so we'll make a * special check for this: * Two IPv6 addresses with the same prefix is assigned * to a single interrface. * # ifconfig if0 inet6 3ffe:0501::1 prefix 64 alias (*1) * # ifconfig if0 inet6 3ffe:0501::2 prefix 64 alias (*2) * In this case, (*1) and (*2) want to add the same * net route entry, 3ffe:0501:: -> if0. * This case should not raise an error. */ rt2 = rtalloc1((struct sockaddr *)sin6, 0, RTF_CLONING | RTF_PRCLONING); if (rt2) { if ((rt2->rt_flags & (RTF_CLONING|RTF_HOST|RTF_GATEWAY)) == RTF_CLONING && rt2->rt_gateway && rt2->rt_gateway->sa_family == AF_LINK && rt2->rt_ifp == rt->rt_ifp) { ret = rt2->rt_nodes; } RTFREE(rt2); } } return ret; } /* * This code is the inverse of in6_clsroute: on first reference, if we * were managing the route, stop doing so and set the expiration timer * back off again. */ static struct radix_node * in6_matroute(void *v_arg, struct radix_node_head *head) { struct radix_node *rn = rn_match(v_arg, head); struct rtentry *rt = (struct rtentry *)rn; if (rt && rt->rt_refcnt == 0) { /* this is first reference */ if (rt->rt_flags & RTPRF_OURS) { rt->rt_flags &= ~RTPRF_OURS; rt->rt_rmx.rmx_expire = 0; } } return rn; } static int rtq_reallyold = 60*60; /* one hour is ``really old'' */ SYSCTL_INT(_net_inet_ip, IPCTL_RTEXPIRE, rtexpire, CTLFLAG_RW, &rtq_reallyold , 0, ""); static int rtq_minreallyold = 10; /* never automatically crank down to less */ SYSCTL_INT(_net_inet_ip, IPCTL_RTMINEXPIRE, rtminexpire, CTLFLAG_RW, &rtq_minreallyold , 0, ""); static int rtq_toomany = 128; /* 128 cached routes is ``too many'' */ SYSCTL_INT(_net_inet_ip, IPCTL_RTMAXCACHE, rtmaxcache, CTLFLAG_RW, &rtq_toomany , 0, ""); /* * On last reference drop, mark the route as belong to us so that it can be * timed out. */ static void in6_clsroute(struct radix_node *rn, struct radix_node_head *head) { struct rtentry *rt = (struct rtentry *)rn; if (!(rt->rt_flags & RTF_UP)) return; /* prophylactic measures */ if ((rt->rt_flags & (RTF_LLINFO | RTF_HOST)) != RTF_HOST) return; if ((rt->rt_flags & (RTF_WASCLONED | RTPRF_OURS)) != RTF_WASCLONED) return; /* * As requested by David Greenman: * If rtq_reallyold is 0, just delete the route without * waiting for a timeout cycle to kill it. */ if (rtq_reallyold != 0) { rt->rt_flags |= RTPRF_OURS; rt->rt_rmx.rmx_expire = time_second + rtq_reallyold; } else { rtrequest(RTM_DELETE, (struct sockaddr *)rt_key(rt), rt->rt_gateway, rt_mask(rt), rt->rt_flags, 0); } } struct rtqk_arg { struct radix_node_head *rnh; int mode; int updating; int draining; int killed; int found; time_t nextstop; }; /* * Get rid of old routes. When draining, this deletes everything, even when * the timeout is not expired yet. When updating, this makes sure that * nothing has a timeout longer than the current value of rtq_reallyold. */ static int in6_rtqkill(struct radix_node *rn, void *rock) { struct rtqk_arg *ap = rock; struct rtentry *rt = (struct rtentry *)rn; int err; if (rt->rt_flags & RTPRF_OURS) { ap->found++; if (ap->draining || rt->rt_rmx.rmx_expire <= time_second) { if (rt->rt_refcnt > 0) panic("rtqkill route really not free"); err = rtrequest(RTM_DELETE, (struct sockaddr *)rt_key(rt), rt->rt_gateway, rt_mask(rt), rt->rt_flags, 0); if (err) { log(LOG_WARNING, "in6_rtqkill: error %d", err); } else { ap->killed++; } } else { if (ap->updating && (rt->rt_rmx.rmx_expire - time_second > rtq_reallyold)) { rt->rt_rmx.rmx_expire = time_second + rtq_reallyold; } ap->nextstop = lmin(ap->nextstop, rt->rt_rmx.rmx_expire); } } return 0; } #define RTQ_TIMEOUT 60*10 /* run no less than once every ten minutes */ static int rtq_timeout = RTQ_TIMEOUT; static void in6_rtqtimo(void *rock) { struct radix_node_head *rnh = rock; struct rtqk_arg arg; struct timeval atv; static time_t last_adjusted_timeout = 0; int s; arg.found = arg.killed = 0; arg.rnh = rnh; arg.nextstop = time_second + rtq_timeout; arg.draining = arg.updating = 0; s = splnet(); rnh->rnh_walktree(rnh, in6_rtqkill, &arg); splx(s); /* * Attempt to be somewhat dynamic about this: * If there are ``too many'' routes sitting around taking up space, * then crank down the timeout, and see if we can't make some more * go away. However, we make sure that we will never adjust more * than once in rtq_timeout seconds, to keep from cranking down too * hard. */ if ((arg.found - arg.killed > rtq_toomany) && (time_second - last_adjusted_timeout >= rtq_timeout) && rtq_reallyold > rtq_minreallyold) { rtq_reallyold = 2*rtq_reallyold / 3; if (rtq_reallyold < rtq_minreallyold) { rtq_reallyold = rtq_minreallyold; } last_adjusted_timeout = time_second; #ifdef DIAGNOSTIC log(LOG_DEBUG, "in6_rtqtimo: adjusted rtq_reallyold to %d", rtq_reallyold); #endif arg.found = arg.killed = 0; arg.updating = 1; s = splnet(); rnh->rnh_walktree(rnh, in6_rtqkill, &arg); splx(s); } atv.tv_usec = 0; atv.tv_sec = arg.nextstop; timeout(in6_rtqtimo, rock, tvtohz(&atv)); } /* * Age old PMTUs. */ struct mtuex_arg { struct radix_node_head *rnh; time_t nextstop; }; static int in6_mtuexpire(struct radix_node *rn, void *rock) { struct rtentry *rt = (struct rtentry *)rn; struct mtuex_arg *ap = rock; /* sanity */ if (!rt) panic("rt == NULL in in6_mtuexpire"); if (rt->rt_rmx.rmx_expire && !(rt->rt_flags & RTF_PROBEMTU)) { if (rt->rt_rmx.rmx_expire <= time_second) { rt->rt_flags |= RTF_PROBEMTU; } else { ap->nextstop = lmin(ap->nextstop, rt->rt_rmx.rmx_expire); } } return 0; } #define MTUTIMO_DEFAULT (60*1) static void in6_mtutimo(void *rock) { struct radix_node_head *rnh = rock; struct mtuex_arg arg; struct timeval atv; int s; arg.rnh = rnh; arg.nextstop = time_second + MTUTIMO_DEFAULT; s = splnet(); rnh->rnh_walktree(rnh, in6_mtuexpire, &arg); splx(s); atv.tv_usec = 0; atv.tv_sec = arg.nextstop; if (atv.tv_sec < time_second) { printf("invalid mtu expiration time on routing table\n"); arg.nextstop = time_second + 30; /*last resort*/ } timeout(in6_mtutimo, rock, tvtohz(&atv)); } + +#if 0 +void +in6_rtqdrain() +{ + struct radix_node_head *rnh = rt_tables[AF_INET6]; + struct rtqk_arg arg; + int s; + arg.found = arg.killed = 0; + arg.rnh = rnh; + arg.nextstop = 0; + arg.draining = 1; + arg.updating = 0; + s = splnet(); + rnh->rnh_walktree(rnh, in6_rtqkill, &arg); + splx(s); +} +#endif /* * Initialize our routing tree. */ int in6_inithead(void **head, int off) { struct radix_node_head *rnh; if (!rn_inithead(head, off)) return 0; if (head != (void **)&rt_tables[AF_INET6]) /* BOGUS! */ return 1; /* only do this for the real routing table */ rnh = *head; rnh->rnh_addaddr = in6_addroute; rnh->rnh_matchaddr = in6_matroute; rnh->rnh_close = in6_clsroute; in6_rtqtimo(rnh); /* kick off timeout first time */ in6_mtutimo(rnh); /* kick off timeout first time */ return 1; } Index: head/sys/netinet6/in6_src.c =================================================================== --- head/sys/netinet6/in6_src.c (nonexistent) +++ head/sys/netinet6/in6_src.c (revision 62587) @@ -0,0 +1,550 @@ +/* $FreeBSD$ */ +/* $KAME: in6_src.c,v 1.27 2000/06/21 08:07:13 itojun Exp $ */ + +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Copyright (c) 1982, 1986, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)in_pcb.c 8.2 (Berkeley) 1/4/94 + */ + +#include "opt_inet.h" +#include "opt_inet6.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef ENABLE_DEFAULT_SCOPE +#include +#endif + +#include + +#include "loop.h" + +/* + * Return an IPv6 address, which is the most appropriate for given + * destination and user specified options. + * If necessary, this function lookups the routing table and return + * an entry to the caller for later use. + */ +struct in6_addr * +in6_selectsrc(dstsock, opts, mopts, ro, laddr, errorp) + struct sockaddr_in6 *dstsock; + struct ip6_pktopts *opts; + struct ip6_moptions *mopts; + struct route_in6 *ro; + struct in6_addr *laddr; + int *errorp; +{ + struct in6_addr *dst; + struct in6_ifaddr *ia6 = 0; + struct in6_pktinfo *pi = NULL; + + dst = &dstsock->sin6_addr; + *errorp = 0; + + /* + * If the source address is explicitly specified by the caller, + * use it. + */ + if (opts && (pi = opts->ip6po_pktinfo) && + !IN6_IS_ADDR_UNSPECIFIED(&pi->ipi6_addr)) + return(&pi->ipi6_addr); + + /* + * If the source address is not specified but the socket(if any) + * is already bound, use the bound address. + */ + if (laddr && !IN6_IS_ADDR_UNSPECIFIED(laddr)) + return(laddr); + + /* + * If the caller doesn't specify the source address but + * the outgoing interface, use an address associated with + * the interface. + */ + if (pi && pi->ipi6_ifindex) { + /* XXX boundary check is assumed to be already done. */ + ia6 = in6_ifawithscope(ifindex2ifnet[pi->ipi6_ifindex], + dst); + if (ia6 == 0) { + *errorp = EADDRNOTAVAIL; + return(0); + } + return(&satosin6(&ia6->ia_addr)->sin6_addr); + } + + /* + * If the destination address is a link-local unicast address or + * a multicast address, and if the outgoing interface is specified + * by the sin6_scope_id filed, use an address associated with the + * interface. + * XXX: We're now trying to define more specific semantics of + * sin6_scope_id field, so this part will be rewritten in + * the near future. + */ + if ((IN6_IS_ADDR_LINKLOCAL(dst) || IN6_IS_ADDR_MULTICAST(dst)) && + dstsock->sin6_scope_id) { + /* + * I'm not sure if boundary check for scope_id is done + * somewhere... + */ + if (dstsock->sin6_scope_id < 0 || + if_index < dstsock->sin6_scope_id) { + *errorp = ENXIO; /* XXX: better error? */ + return(0); + } + ia6 = in6_ifawithscope(ifindex2ifnet[dstsock->sin6_scope_id], + dst); + if (ia6 == 0) { + *errorp = EADDRNOTAVAIL; + return(0); + } + return(&satosin6(&ia6->ia_addr)->sin6_addr); + } + + /* + * If the destination address is a multicast address and + * the outgoing interface for the address is specified + * by the caller, use an address associated with the interface. + * There is a sanity check here; if the destination has node-local + * scope, the outgoing interfacde should be a loopback address. + * Even if the outgoing interface is not specified, we also + * choose a loopback interface as the outgoing interface. + */ + if (IN6_IS_ADDR_MULTICAST(dst)) { + struct ifnet *ifp = mopts ? mopts->im6o_multicast_ifp : NULL; + + if (ifp == NULL && IN6_IS_ADDR_MC_NODELOCAL(dst)) { + ifp = &loif[0]; + } + + if (ifp) { + ia6 = in6_ifawithscope(ifp, dst); + if (ia6 == 0) { + *errorp = EADDRNOTAVAIL; + return(0); + } + return(&satosin6(&ia6->ia_addr)->sin6_addr); + } + } + + /* + * If the next hop address for the packet is specified + * by caller, use an address associated with the route + * to the next hop. + */ + { + struct sockaddr_in6 *sin6_next; + struct rtentry *rt; + + if (opts && opts->ip6po_nexthop) { + sin6_next = satosin6(opts->ip6po_nexthop); + rt = nd6_lookup(&sin6_next->sin6_addr, 1, NULL); + if (rt) { + ia6 = in6_ifawithscope(rt->rt_ifp, dst); + if (ia6 == 0) + ia6 = ifatoia6(rt->rt_ifa); + } + if (ia6 == 0) { + *errorp = EADDRNOTAVAIL; + return(0); + } + return(&satosin6(&ia6->ia_addr)->sin6_addr); + } + } + + /* + * If route is known or can be allocated now, + * our src addr is taken from the i/f, else punt. + */ + if (ro) { + if (ro->ro_rt && + !IN6_ARE_ADDR_EQUAL(&satosin6(&ro->ro_dst)->sin6_addr, dst)) { + RTFREE(ro->ro_rt); + ro->ro_rt = (struct rtentry *)0; + } + if (ro->ro_rt == (struct rtentry *)0 || + ro->ro_rt->rt_ifp == (struct ifnet *)0) { + /* No route yet, so try to acquire one */ + bzero(&ro->ro_dst, sizeof(struct sockaddr_in6)); + ro->ro_dst.sin6_family = AF_INET6; + ro->ro_dst.sin6_len = sizeof(struct sockaddr_in6); + ro->ro_dst.sin6_addr = *dst; + ro->ro_dst.sin6_scope_id = dstsock->sin6_scope_id; + if (IN6_IS_ADDR_MULTICAST(dst)) { + ro->ro_rt = rtalloc1(&((struct route *)ro) + ->ro_dst, 0, 0UL); + } else { + rtalloc((struct route *)ro); + } + } + + /* + * in_pcbconnect() checks out IFF_LOOPBACK to skip using + * the address. But we don't know why it does so. + * It is necessary to ensure the scope even for lo0 + * so doesn't check out IFF_LOOPBACK. + */ + + if (ro->ro_rt) { + ia6 = in6_ifawithscope(ro->ro_rt->rt_ifa->ifa_ifp, dst); + if (ia6 == 0) /* xxx scope error ?*/ + ia6 = ifatoia6(ro->ro_rt->rt_ifa); + } +#if 0 + /* + * xxx The followings are necessary? (kazu) + * I don't think so. + * It's for SO_DONTROUTE option in IPv4.(jinmei) + */ + if (ia6 == 0) { + struct sockaddr_in6 sin6 = {sizeof(sin6), AF_INET6, 0}; + + sin6->sin6_addr = *dst; + + ia6 = ifatoia6(ifa_ifwithdstaddr(sin6tosa(&sin6))); + if (ia6 == 0) + ia6 = ifatoia6(ifa_ifwithnet(sin6tosa(&sin6))); + if (ia6 == 0) + return(0); + return(&satosin6(&ia6->ia_addr)->sin6_addr); + } +#endif /* 0 */ + if (ia6 == 0) { + *errorp = EHOSTUNREACH; /* no route */ + return(0); + } + return(&satosin6(&ia6->ia_addr)->sin6_addr); + } + + *errorp = EADDRNOTAVAIL; + return(0); +} + +/* + * Default hop limit selection. The precedence is as follows: + * 1. Hoplimit value specified via ioctl. + * 2. (If the outgoing interface is detected) the current + * hop limit of the interface specified by router advertisement. + * 3. The system default hoplimit. +*/ +int +in6_selecthlim(in6p, ifp) + struct in6pcb *in6p; + struct ifnet *ifp; +{ + if (in6p && in6p->in6p_hops >= 0) + return(in6p->in6p_hops); + else if (ifp) + return(nd_ifinfo[ifp->if_index].chlim); + else + return(ip6_defhlim); +} + +/* + * XXX: this is borrowed from in6_pcbbind(). If possible, we should + * share this function by all *bsd*... + */ +int +in6_pcbsetport(laddr, inp, p) + struct in6_addr *laddr; + struct inpcb *inp; + struct proc *p; +{ + struct socket *so = inp->inp_socket; + u_int16_t lport = 0, first, last, *lastport; + int count, error = 0, wild = 0; + struct inpcbinfo *pcbinfo = inp->inp_pcbinfo; + + /* XXX: this is redundant when called from in6_pcbbind */ + if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0) + wild = INPLOOKUP_WILDCARD; + + inp->inp_flags |= INP_ANONPORT; + + if (inp->inp_flags & INP_HIGHPORT) { + first = ipport_hifirstauto; /* sysctl */ + last = ipport_hilastauto; + lastport = &pcbinfo->lasthi; + } else if (inp->inp_flags & INP_LOWPORT) { + if (p && (error = suser(p))) + return error; + first = ipport_lowfirstauto; /* 1023 */ + last = ipport_lowlastauto; /* 600 */ + lastport = &pcbinfo->lastlow; + } else { + first = ipport_firstauto; /* sysctl */ + last = ipport_lastauto; + lastport = &pcbinfo->lastport; + } + /* + * Simple check to ensure all ports are not used up causing + * a deadlock here. + * + * We split the two cases (up and down) so that the direction + * is not being tested on each round of the loop. + */ + if (first > last) { + /* + * counting down + */ + count = first - last; + + do { + if (count-- < 0) { /* completely used? */ + /* + * Undo any address bind that may have + * occurred above. + */ + inp->in6p_laddr = in6addr_any; + return (EAGAIN); + } + --*lastport; + if (*lastport > first || *lastport < last) + *lastport = first; + lport = htons(*lastport); + } while (in6_pcblookup_local(pcbinfo, + &inp->in6p_laddr, lport, wild)); + } else { + /* + * counting up + */ + count = last - first; + + do { + if (count-- < 0) { /* completely used? */ + /* + * Undo any address bind that may have + * occurred above. + */ + inp->in6p_laddr = in6addr_any; + return (EAGAIN); + } + ++*lastport; + if (*lastport < first || *lastport > last) + *lastport = first; + lport = htons(*lastport); + } while (in6_pcblookup_local(pcbinfo, + &inp->in6p_laddr, lport, wild)); + } + + inp->inp_lport = lport; + if (in_pcbinshash(inp) != 0) { + inp->in6p_laddr = in6addr_any; + inp->inp_lport = 0; + return (EAGAIN); + } + + return(0); +} + +/* + * generate kernel-internal form (scopeid embedded into s6_addr16[1]). + * If the address scope of is link-local, embed the interface index in the + * address. The routine determines our precedence + * between advanced API scope/interface specification and basic API + * specification. + * + * this function should be nuked in the future, when we get rid of + * embedded scopeid thing. + * + * XXX actually, it is over-specification to return ifp against sin6_scope_id. + * there can be multiple interfaces that belong to a particular scope zone + * (in specification, we have 1:N mapping between a scope zone and interfaces). + * we may want to change the function to return something other than ifp. + */ +int +in6_embedscope(in6, sin6, in6p, ifpp) + struct in6_addr *in6; + const struct sockaddr_in6 *sin6; +#ifdef HAVE_NRL_INPCB + struct inpcb *in6p; +#define in6p_outputopts inp_outputopts6 +#define in6p_moptions inp_moptions6 +#else + struct in6pcb *in6p; +#endif + struct ifnet **ifpp; +{ + struct ifnet *ifp = NULL; + u_int32_t scopeid; + + *in6 = sin6->sin6_addr; + scopeid = sin6->sin6_scope_id; + if (ifpp) + *ifpp = NULL; + + /* + * don't try to read sin6->sin6_addr beyond here, since the caller may + * ask us to overwrite existing sockaddr_in6 + */ + +#ifdef ENABLE_DEFAULT_SCOPE + if (scopeid == 0) + scopeid = scope6_addr2default(in6); +#endif + + if (IN6_IS_SCOPE_LINKLOCAL(in6)) { + struct in6_pktinfo *pi; + + /* + * KAME assumption: link id == interface id + */ + + if (in6p && in6p->in6p_outputopts && + (pi = in6p->in6p_outputopts->ip6po_pktinfo) && + pi->ipi6_ifindex) { + ifp = ifindex2ifnet[pi->ipi6_ifindex]; + in6->s6_addr16[1] = htons(pi->ipi6_ifindex); + } else if (in6p && IN6_IS_ADDR_MULTICAST(in6) && + in6p->in6p_moptions && + in6p->in6p_moptions->im6o_multicast_ifp) { + ifp = in6p->in6p_moptions->im6o_multicast_ifp; + in6->s6_addr16[1] = htons(ifp->if_index); + } else if (scopeid) { + /* boundary check */ + if (scopeid < 0 || if_index < scopeid) + return ENXIO; /* XXX EINVAL? */ + ifp = ifindex2ifnet[scopeid]; + /*XXX assignment to 16bit from 32bit variable */ + in6->s6_addr16[1] = htons(scopeid & 0xffff); + } + + if (ifpp) + *ifpp = ifp; + } + + return 0; +} +#ifdef HAVE_NRL_INPCB +#undef in6p_outputopts +#undef in6p_moptions +#endif + +/* + * generate standard sockaddr_in6 from embedded form. + * touches sin6_addr and sin6_scope_id only. + * + * this function should be nuked in the future, when we get rid of + * embedded scopeid thing. + */ +int +in6_recoverscope(sin6, in6, ifp) + struct sockaddr_in6 *sin6; + const struct in6_addr *in6; + struct ifnet *ifp; +{ + u_int32_t scopeid; + + sin6->sin6_addr = *in6; + + /* + * don't try to read *in6 beyond here, since the caller may + * ask us to overwrite existing sockaddr_in6 + */ + + sin6->sin6_scope_id = 0; + if (IN6_IS_SCOPE_LINKLOCAL(in6)) { + /* + * KAME assumption: link id == interface id + */ + scopeid = ntohs(sin6->sin6_addr.s6_addr16[1]); + if (scopeid) { + /* sanity check */ + if (scopeid < 0 || if_index < scopeid) + return ENXIO; +#ifndef FAKE_LOOPBACK_IF + if (ifp && (ifp->if_flags & IFF_LOOPBACK) == 0 && + ifp->if_index != scopeid) { + return ENXIO; + } +#else + if (ifp && ifp->if_index != scopeid) + return ENXIO; +#endif + sin6->sin6_addr.s6_addr16[1] = 0; + sin6->sin6_scope_id = scopeid; + } + } + + return 0; +} Property changes on: head/sys/netinet6/in6_src.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: head/sys/netinet6/in6_var.h =================================================================== --- head/sys/netinet6/in6_var.h (revision 62586) +++ head/sys/netinet6/in6_var.h (revision 62587) @@ -1,559 +1,585 @@ +/* $FreeBSD$ */ +/* $KAME: in6_var.h,v 1.33 2000/05/17 05:07:26 jinmei Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Copyright (c) 1985, 1986, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)in_var.h 8.1 (Berkeley) 6/10/93 - * $FreeBSD$ */ -#ifndef _NETINET6_IN6_VAR_H_ -#define _NETINET6_IN6_VAR_H_ +#ifndef _NETINET6_IN6_VAR_H_ +#define _NETINET6_IN6_VAR_H_ /* * Interface address, Internet version. One of these structures * is allocated for each interface with an Internet address. * The ifaddr structure contains the protocol-independent part * of the structure and is assumed to be first. */ /* * pltime/vltime are just for future reference (required to implements 2 * hour rule for hosts). they should never be modified by nd6_timeout or * anywhere else. * userland -> kernel: accept pltime/vltime * kernel -> userland: throuw up everything * in kernel: modify preferred/expire only */ struct in6_addrlifetime { - time_t ia6t_expire; /* valid lifetime expiration time */ - time_t ia6t_preferred; /* preferred lifetime expiration time */ - u_int32_t ia6t_vltime; /* valid lifetime */ - u_int32_t ia6t_pltime; /* prefix lifetime */ + time_t ia6t_expire; /* valid lifetime expiration time */ + time_t ia6t_preferred; /* preferred lifetime expiration time */ + u_int32_t ia6t_vltime; /* valid lifetime */ + u_int32_t ia6t_pltime; /* prefix lifetime */ }; struct in6_ifaddr { struct ifaddr ia_ifa; /* protocol-independent info */ #define ia_ifp ia_ifa.ifa_ifp -#define ia_flags ia_ifa.ifa_flags +#define ia_flags ia_ifa.ifa_flags struct sockaddr_in6 ia_addr; /* interface address */ struct sockaddr_in6 ia_net; /* network number of interface */ struct sockaddr_in6 ia_dstaddr; /* space for destination addr */ struct sockaddr_in6 ia_prefixmask; /* prefix mask */ - u_int32_t ia_plen; /* prefix length */ + u_int32_t ia_plen; /* prefix length */ struct in6_ifaddr *ia_next; /* next in6 list of IP6 addresses */ int ia6_flags; - struct in6_addrlifetime ia6_lifetime; /* NULL = infty */ - struct ifprefix *ia6_ifpr; /* back pointer to ifprefix */ + struct in6_addrlifetime ia6_lifetime; /* NULL = infty */ + struct ifprefix *ia6_ifpr; /* back pointer to ifprefix */ }; /* * IPv6 interface statistics, as defined in RFC2465 Ipv6IfStatsEntry (p12). */ struct in6_ifstat { - u_int64_t ifs6_in_receive; /* # of total input datagram */ - u_int64_t ifs6_in_hdrerr; /* # of datagrams with invalid hdr */ - u_int64_t ifs6_in_toobig; /* # of datagrams exceeded MTU */ - u_int64_t ifs6_in_noroute; /* # of datagrams with no route */ - u_int64_t ifs6_in_addrerr; /* # of datagrams with invalid dst */ - u_int64_t ifs6_in_protounknown; /* # of datagrams with unknown proto */ + u_quad_t ifs6_in_receive; /* # of total input datagram */ + u_quad_t ifs6_in_hdrerr; /* # of datagrams with invalid hdr */ + u_quad_t ifs6_in_toobig; /* # of datagrams exceeded MTU */ + u_quad_t ifs6_in_noroute; /* # of datagrams with no route */ + u_quad_t ifs6_in_addrerr; /* # of datagrams with invalid dst */ + u_quad_t ifs6_in_protounknown; /* # of datagrams with unknown proto */ /* NOTE: increment on final dst if */ - u_int64_t ifs6_in_truncated; /* # of truncated datagrams */ - u_int64_t ifs6_in_discard; /* # of discarded datagrams */ + u_quad_t ifs6_in_truncated; /* # of truncated datagrams */ + u_quad_t ifs6_in_discard; /* # of discarded datagrams */ /* NOTE: fragment timeout is not here */ - u_int64_t ifs6_in_deliver; /* # of datagrams delivered to ULP */ + u_quad_t ifs6_in_deliver; /* # of datagrams delivered to ULP */ /* NOTE: increment on final dst if */ - u_int64_t ifs6_out_forward; /* # of datagrams forwarded */ + u_quad_t ifs6_out_forward; /* # of datagrams forwarded */ /* NOTE: increment on outgoing if */ - u_int64_t ifs6_out_request; /* # of outgoing datagrams from ULP */ + u_quad_t ifs6_out_request; /* # of outgoing datagrams from ULP */ /* NOTE: does not include forwrads */ - u_int64_t ifs6_out_discard; /* # of discarded datagrams */ - u_int64_t ifs6_out_fragok; /* # of datagrams fragmented */ - u_int64_t ifs6_out_fragfail; /* # of datagrams failed on fragment */ - u_int64_t ifs6_out_fragcreat; /* # of fragment datagrams */ + u_quad_t ifs6_out_discard; /* # of discarded datagrams */ + u_quad_t ifs6_out_fragok; /* # of datagrams fragmented */ + u_quad_t ifs6_out_fragfail; /* # of datagrams failed on fragment */ + u_quad_t ifs6_out_fragcreat; /* # of fragment datagrams */ /* NOTE: this is # after fragment */ - u_int64_t ifs6_reass_reqd; /* # of incoming fragmented packets */ + u_quad_t ifs6_reass_reqd; /* # of incoming fragmented packets */ /* NOTE: increment on final dst if */ - u_int64_t ifs6_reass_ok; /* # of reassembled packets */ + u_quad_t ifs6_reass_ok; /* # of reassembled packets */ /* NOTE: this is # after reass */ /* NOTE: increment on final dst if */ - u_int64_t ifs6_reass_fail; /* # of reass failures */ + u_quad_t ifs6_reass_fail; /* # of reass failures */ /* NOTE: may not be packet count */ /* NOTE: increment on final dst if */ - u_int64_t ifs6_in_mcast; /* # of inbound multicast datagrams */ - u_int64_t ifs6_out_mcast; /* # of outbound multicast datagrams */ + u_quad_t ifs6_in_mcast; /* # of inbound multicast datagrams */ + u_quad_t ifs6_out_mcast; /* # of outbound multicast datagrams */ }; /* * ICMPv6 interface statistics, as defined in RFC2466 Ipv6IfIcmpEntry. * XXX: I'm not sure if this file is the right place for this structure... */ struct icmp6_ifstat { /* * Input statistics */ /* ipv6IfIcmpInMsgs, total # of input messages */ - u_int64_t ifs6_in_msg; + u_quad_t ifs6_in_msg; /* ipv6IfIcmpInErrors, # of input error messages */ - u_int64_t ifs6_in_error; + u_quad_t ifs6_in_error; /* ipv6IfIcmpInDestUnreachs, # of input dest unreach errors */ - u_int64_t ifs6_in_dstunreach; + u_quad_t ifs6_in_dstunreach; /* ipv6IfIcmpInAdminProhibs, # of input administratively prohibited errs */ - u_int64_t ifs6_in_adminprohib; + u_quad_t ifs6_in_adminprohib; /* ipv6IfIcmpInTimeExcds, # of input time exceeded errors */ - u_int64_t ifs6_in_timeexceed; + u_quad_t ifs6_in_timeexceed; /* ipv6IfIcmpInParmProblems, # of input parameter problem errors */ - u_int64_t ifs6_in_paramprob; + u_quad_t ifs6_in_paramprob; /* ipv6IfIcmpInPktTooBigs, # of input packet too big errors */ - u_int64_t ifs6_in_pkttoobig; + u_quad_t ifs6_in_pkttoobig; /* ipv6IfIcmpInEchos, # of input echo requests */ - u_int64_t ifs6_in_echo; + u_quad_t ifs6_in_echo; /* ipv6IfIcmpInEchoReplies, # of input echo replies */ - u_int64_t ifs6_in_echoreply; + u_quad_t ifs6_in_echoreply; /* ipv6IfIcmpInRouterSolicits, # of input router solicitations */ - u_int64_t ifs6_in_routersolicit; + u_quad_t ifs6_in_routersolicit; /* ipv6IfIcmpInRouterAdvertisements, # of input router advertisements */ - u_int64_t ifs6_in_routeradvert; + u_quad_t ifs6_in_routeradvert; /* ipv6IfIcmpInNeighborSolicits, # of input neighbor solicitations */ - u_int64_t ifs6_in_neighborsolicit; + u_quad_t ifs6_in_neighborsolicit; /* ipv6IfIcmpInNeighborAdvertisements, # of input neighbor advertisements */ - u_int64_t ifs6_in_neighboradvert; + u_quad_t ifs6_in_neighboradvert; /* ipv6IfIcmpInRedirects, # of input redirects */ - u_int64_t ifs6_in_redirect; + u_quad_t ifs6_in_redirect; /* ipv6IfIcmpInGroupMembQueries, # of input MLD queries */ - u_int64_t ifs6_in_mldquery; + u_quad_t ifs6_in_mldquery; /* ipv6IfIcmpInGroupMembResponses, # of input MLD reports */ - u_int64_t ifs6_in_mldreport; + u_quad_t ifs6_in_mldreport; /* ipv6IfIcmpInGroupMembReductions, # of input MLD done */ - u_int64_t ifs6_in_mlddone; + u_quad_t ifs6_in_mlddone; /* * Output statistics. We should solve unresolved routing problem... */ /* ipv6IfIcmpOutMsgs, total # of output messages */ - u_int64_t ifs6_out_msg; + u_quad_t ifs6_out_msg; /* ipv6IfIcmpOutErrors, # of output error messages */ - u_int64_t ifs6_out_error; + u_quad_t ifs6_out_error; /* ipv6IfIcmpOutDestUnreachs, # of output dest unreach errors */ - u_int64_t ifs6_out_dstunreach; + u_quad_t ifs6_out_dstunreach; /* ipv6IfIcmpOutAdminProhibs, # of output administratively prohibited errs */ - u_int64_t ifs6_out_adminprohib; + u_quad_t ifs6_out_adminprohib; /* ipv6IfIcmpOutTimeExcds, # of output time exceeded errors */ - u_int64_t ifs6_out_timeexceed; + u_quad_t ifs6_out_timeexceed; /* ipv6IfIcmpOutParmProblems, # of output parameter problem errors */ - u_int64_t ifs6_out_paramprob; + u_quad_t ifs6_out_paramprob; /* ipv6IfIcmpOutPktTooBigs, # of output packet too big errors */ - u_int64_t ifs6_out_pkttoobig; + u_quad_t ifs6_out_pkttoobig; /* ipv6IfIcmpOutEchos, # of output echo requests */ - u_int64_t ifs6_out_echo; + u_quad_t ifs6_out_echo; /* ipv6IfIcmpOutEchoReplies, # of output echo replies */ - u_int64_t ifs6_out_echoreply; + u_quad_t ifs6_out_echoreply; /* ipv6IfIcmpOutRouterSolicits, # of output router solicitations */ - u_int64_t ifs6_out_routersolicit; + u_quad_t ifs6_out_routersolicit; /* ipv6IfIcmpOutRouterAdvertisements, # of output router advertisements */ - u_int64_t ifs6_out_routeradvert; + u_quad_t ifs6_out_routeradvert; /* ipv6IfIcmpOutNeighborSolicits, # of output neighbor solicitations */ - u_int64_t ifs6_out_neighborsolicit; + u_quad_t ifs6_out_neighborsolicit; /* ipv6IfIcmpOutNeighborAdvertisements, # of output neighbor advertisements */ - u_int64_t ifs6_out_neighboradvert; + u_quad_t ifs6_out_neighboradvert; /* ipv6IfIcmpOutRedirects, # of output redirects */ - u_int64_t ifs6_out_redirect; + u_quad_t ifs6_out_redirect; /* ipv6IfIcmpOutGroupMembQueries, # of output MLD queries */ - u_int64_t ifs6_out_mldquery; + u_quad_t ifs6_out_mldquery; /* ipv6IfIcmpOutGroupMembResponses, # of output MLD reports */ - u_int64_t ifs6_out_mldreport; + u_quad_t ifs6_out_mldreport; /* ipv6IfIcmpOutGroupMembReductions, # of output MLD done */ - u_int64_t ifs6_out_mlddone; + u_quad_t ifs6_out_mlddone; }; struct in6_ifreq { char ifr_name[IFNAMSIZ]; union { struct sockaddr_in6 ifru_addr; struct sockaddr_in6 ifru_dstaddr; short ifru_flags; int ifru_flags6; int ifru_metric; caddr_t ifru_data; - struct in6_addrlifetime ifru_lifetime; - struct in6_ifstat ifru_stat; - struct icmp6_ifstat ifru_icmp6stat; + struct in6_addrlifetime ifru_lifetime; + struct in6_ifstat ifru_stat; + struct icmp6_ifstat ifru_icmp6stat; + u_int32_t ifru_scope_id[16]; } ifr_ifru; }; struct in6_aliasreq { char ifra_name[IFNAMSIZ]; struct sockaddr_in6 ifra_addr; struct sockaddr_in6 ifra_dstaddr; struct sockaddr_in6 ifra_prefixmask; int ifra_flags; - struct in6_addrlifetime ifra_lifetime; + struct in6_addrlifetime ifra_lifetime; }; /* prefix type macro */ -#define IN6_PREFIX_ND 1 -#define IN6_PREFIX_RR 2 +#define IN6_PREFIX_ND 1 +#define IN6_PREFIX_RR 2 /* * prefix related flags passed between kernel(NDP related part) and * user land command(ifconfig) and daemon(rtadvd). */ struct in6_prflags { struct prf_ra { - u_char onlink : 1; - u_char autonomous : 1; - u_char reserved : 6; + u_char onlink : 1; + u_char autonomous : 1; + u_char reserved : 6; } prf_ra; - u_char prf_reserved1; - u_short prf_reserved2; + u_char prf_reserved1; + u_short prf_reserved2; /* want to put this on 4byte offset */ struct prf_rr { - u_char decrvalid : 1; - u_char decrprefd : 1; - u_char reserved : 6; + u_char decrvalid : 1; + u_char decrprefd : 1; + u_char reserved : 6; } prf_rr; - u_char prf_reserved3; - u_short prf_reserved4; + u_char prf_reserved3; + u_short prf_reserved4; }; struct in6_prefixreq { char ipr_name[IFNAMSIZ]; u_char ipr_origin; u_char ipr_plen; - u_int32_t ipr_vltime; - u_int32_t ipr_pltime; - struct in6_prflags ipr_flags; + u_int32_t ipr_vltime; + u_int32_t ipr_pltime; + struct in6_prflags ipr_flags; struct sockaddr_in6 ipr_prefix; }; -#define PR_ORIG_RA 0 -#define PR_ORIG_RR 1 -#define PR_ORIG_STATIC 2 -#define PR_ORIG_KERNEL 3 +#define PR_ORIG_RA 0 +#define PR_ORIG_RR 1 +#define PR_ORIG_STATIC 2 +#define PR_ORIG_KERNEL 3 -#define ipr_raf_onlink ipr_flags.prf_ra.onlink -#define ipr_raf_auto ipr_flags.prf_ra.autonomous +#define ipr_raf_onlink ipr_flags.prf_ra.onlink +#define ipr_raf_auto ipr_flags.prf_ra.autonomous -#define ipr_statef_onlink ipr_flags.prf_state.onlink +#define ipr_statef_onlink ipr_flags.prf_state.onlink -#define ipr_rrf_decrvalid ipr_flags.prf_rr.decrvalid -#define ipr_rrf_decrprefd ipr_flags.prf_rr.decrprefd +#define ipr_rrf_decrvalid ipr_flags.prf_rr.decrvalid +#define ipr_rrf_decrprefd ipr_flags.prf_rr.decrprefd struct in6_rrenumreq { char irr_name[IFNAMSIZ]; u_char irr_origin; u_char irr_m_len; /* match len for matchprefix */ u_char irr_m_minlen; /* minlen for matching prefix */ u_char irr_m_maxlen; /* maxlen for matching prefix */ u_char irr_u_uselen; /* uselen for adding prefix */ u_char irr_u_keeplen; /* keeplen from matching prefix */ - struct irr_raflagmask { - u_char onlink : 1; - u_char autonomous : 1; - u_char reserved : 6; + struct irr_raflagmask { + u_char onlink : 1; + u_char autonomous : 1; + u_char reserved : 6; } irr_raflagmask; - u_int32_t irr_vltime; - u_int32_t irr_pltime; - struct in6_prflags irr_flags; + u_int32_t irr_vltime; + u_int32_t irr_pltime; + struct in6_prflags irr_flags; struct sockaddr_in6 irr_matchprefix; struct sockaddr_in6 irr_useprefix; }; -#define irr_raf_mask_onlink irr_raflagmask.onlink -#define irr_raf_mask_auto irr_raflagmask.autonomous -#define irr_raf_mask_reserved irr_raflagmask.reserved +#define irr_raf_mask_onlink irr_raflagmask.onlink +#define irr_raf_mask_auto irr_raflagmask.autonomous +#define irr_raf_mask_reserved irr_raflagmask.reserved -#define irr_raf_onlink irr_flags.prf_ra.onlink -#define irr_raf_auto irr_flags.prf_ra.autonomous +#define irr_raf_onlink irr_flags.prf_ra.onlink +#define irr_raf_auto irr_flags.prf_ra.autonomous -#define irr_statef_onlink irr_flags.prf_state.onlink +#define irr_statef_onlink irr_flags.prf_state.onlink -#define irr_rrf irr_flags.prf_rr -#define irr_rrf_decrvalid irr_flags.prf_rr.decrvalid -#define irr_rrf_decrprefd irr_flags.prf_rr.decrprefd +#define irr_rrf irr_flags.prf_rr +#define irr_rrf_decrvalid irr_flags.prf_rr.decrvalid +#define irr_rrf_decrprefd irr_flags.prf_rr.decrprefd /* * Given a pointer to an in6_ifaddr (ifaddr), * return a pointer to the addr as a sockaddr_in6 */ -#define IA6_IN6(ia) (&((ia)->ia_addr.sin6_addr)) -#define IA6_DSTIN6(ia) (&((ia)->ia_dstaddr.sin6_addr)) -#define IA6_MASKIN6(ia) (&((ia)->ia_prefixmask.sin6_addr)) -#define IA6_SIN6(ia) (&((ia)->ia_addr)) -#define IA6_DSTSIN6(ia) (&((ia)->ia_dstaddr)) -#define IFA_IN6(x) (&((struct sockaddr_in6 *)((x)->ifa_addr))->sin6_addr) -#define IFA_DSTIN6(x) (&((struct sockaddr_in6 *)((x)->ifa_dstaddr))->sin6_addr) +#define IA6_IN6(ia) (&((ia)->ia_addr.sin6_addr)) +#define IA6_DSTIN6(ia) (&((ia)->ia_dstaddr.sin6_addr)) +#define IA6_MASKIN6(ia) (&((ia)->ia_prefixmask.sin6_addr)) +#define IA6_SIN6(ia) (&((ia)->ia_addr)) +#define IA6_DSTSIN6(ia) (&((ia)->ia_dstaddr)) +#define IFA_IN6(x) (&((struct sockaddr_in6 *)((x)->ifa_addr))->sin6_addr) +#define IFA_DSTIN6(x) (&((struct sockaddr_in6 *)((x)->ifa_dstaddr))->sin6_addr) -#define IFPR_IN6(x) (&((struct sockaddr_in6 *)((x)->ifpr_prefix))->sin6_addr) +#define IFPR_IN6(x) (&((struct sockaddr_in6 *)((x)->ifpr_prefix))->sin6_addr) #ifdef _KERNEL -#define IN6_ARE_MASKED_ADDR_EQUAL(d, a, m) ( \ +#define IN6_ARE_MASKED_ADDR_EQUAL(d, a, m) ( \ (((d)->s6_addr32[0] ^ (a)->s6_addr32[0]) & (m)->s6_addr32[0]) == 0 && \ (((d)->s6_addr32[1] ^ (a)->s6_addr32[1]) & (m)->s6_addr32[1]) == 0 && \ (((d)->s6_addr32[2] ^ (a)->s6_addr32[2]) & (m)->s6_addr32[2]) == 0 && \ (((d)->s6_addr32[3] ^ (a)->s6_addr32[3]) & (m)->s6_addr32[3]) == 0 ) #endif -#define SIOCSIFADDR_IN6 _IOW('i', 12, struct in6_ifreq) -#define SIOCGIFADDR_IN6 _IOWR('i', 33, struct in6_ifreq) -#define SIOCSIFDSTADDR_IN6 _IOW('i', 14, struct in6_ifreq) -#define SIOCGIFDSTADDR_IN6 _IOWR('i', 34, struct in6_ifreq) -#define SIOCSIFNETMASK_IN6 _IOW('i', 22, struct in6_ifreq) -#define SIOCGIFNETMASK_IN6 _IOWR('i', 37, struct in6_ifreq) +#define SIOCSIFADDR_IN6 _IOW('i', 12, struct in6_ifreq) +#define SIOCGIFADDR_IN6 _IOWR('i', 33, struct in6_ifreq) -#define SIOCDIFADDR_IN6 _IOW('i', 25, struct in6_ifreq) -#define SIOCAIFADDR_IN6 _IOW('i', 26, struct in6_aliasreq) +#ifdef _KERNEL +/* + * SIOCSxxx ioctls should be unused (see comments in in6.c), but + * we do not shift numbers for binary compatibility. + */ +#define SIOCSIFDSTADDR_IN6 _IOW('i', 14, struct in6_ifreq) +#define SIOCSIFNETMASK_IN6 _IOW('i', 22, struct in6_ifreq) +#endif -#define SIOCSIFPHYADDR_IN6 _IOW('i', 70, struct in6_aliasreq) +#define SIOCGIFDSTADDR_IN6 _IOWR('i', 34, struct in6_ifreq) +#define SIOCGIFNETMASK_IN6 _IOWR('i', 37, struct in6_ifreq) + +#define SIOCDIFADDR_IN6 _IOW('i', 25, struct in6_ifreq) +#define SIOCAIFADDR_IN6 _IOW('i', 26, struct in6_aliasreq) + +#define SIOCSIFPHYADDR_IN6 _IOW('i', 70, struct in6_aliasreq) #define SIOCGIFPSRCADDR_IN6 _IOWR('i', 71, struct in6_ifreq) #define SIOCGIFPDSTADDR_IN6 _IOWR('i', 72, struct in6_ifreq) -#define SIOCGIFAFLAG_IN6 _IOWR('i', 73, struct in6_ifreq) +#define SIOCGIFAFLAG_IN6 _IOWR('i', 73, struct in6_ifreq) -#define SIOCGDRLST_IN6 _IOWR('i', 74, struct in6_drlist) -#define SIOCGPRLST_IN6 _IOWR('i', 75, struct in6_prlist) -#define SIOCGIFINFO_IN6 _IOWR('i', 76, struct in6_ndireq) -#define SIOCSNDFLUSH_IN6 _IOWR('i', 77, struct in6_ifreq) -#define SIOCGNBRINFO_IN6 _IOWR('i', 78, struct in6_nbrinfo) -#define SIOCSPFXFLUSH_IN6 _IOWR('i', 79, struct in6_ifreq) -#define SIOCSRTRFLUSH_IN6 _IOWR('i', 80, struct in6_ifreq) +#define SIOCGDRLST_IN6 _IOWR('i', 74, struct in6_drlist) +#define SIOCGPRLST_IN6 _IOWR('i', 75, struct in6_prlist) +#define SIOCGIFINFO_IN6 _IOWR('i', 76, struct in6_ndireq) +#define SIOCSNDFLUSH_IN6 _IOWR('i', 77, struct in6_ifreq) +#define SIOCGNBRINFO_IN6 _IOWR('i', 78, struct in6_nbrinfo) +#define SIOCSPFXFLUSH_IN6 _IOWR('i', 79, struct in6_ifreq) +#define SIOCSRTRFLUSH_IN6 _IOWR('i', 80, struct in6_ifreq) -#define SIOCGIFALIFETIME_IN6 _IOWR('i', 81, struct in6_ifreq) -#define SIOCSIFALIFETIME_IN6 _IOWR('i', 82, struct in6_ifreq) -#define SIOCGIFSTAT_IN6 _IOWR('i', 83, struct in6_ifreq) -#define SIOCGIFSTAT_ICMP6 _IOWR('i', 84, struct in6_ifreq) +#define SIOCGIFALIFETIME_IN6 _IOWR('i', 81, struct in6_ifreq) +#define SIOCSIFALIFETIME_IN6 _IOWR('i', 82, struct in6_ifreq) +#define SIOCGIFSTAT_IN6 _IOWR('i', 83, struct in6_ifreq) +#define SIOCGIFSTAT_ICMP6 _IOWR('i', 84, struct in6_ifreq) -#define SIOCSIFPREFIX_IN6 _IOW('i', 100, struct in6_prefixreq) /* set */ -#define SIOCGIFPREFIX_IN6 _IOWR('i', 101, struct in6_prefixreq) /* get */ -#define SIOCDIFPREFIX_IN6 _IOW('i', 102, struct in6_prefixreq) /* del */ -#define SIOCAIFPREFIX_IN6 _IOW('i', 103, struct in6_rrenumreq) /* add */ -#define SIOCCIFPREFIX_IN6 _IOW('i', 104, \ +#define SIOCSDEFIFACE_IN6 _IOWR('i', 85, struct in6_ndifreq) +#define SIOCGDEFIFACE_IN6 _IOWR('i', 86, struct in6_ndifreq) + +#define SIOCSIFINFO_FLAGS _IOWR('i', 87, struct in6_ndireq) /* XXX */ + +#define SIOCSSCOPE6 _IOW('i', 88, struct in6_ifreq) +#define SIOCGSCOPE6 _IOWR('i', 89, struct in6_ifreq) +#define SIOCGSCOPE6DEF _IOWR('i', 90, struct in6_ifreq) + +#define SIOCSIFPREFIX_IN6 _IOW('i', 100, struct in6_prefixreq) /* set */ +#define SIOCGIFPREFIX_IN6 _IOWR('i', 101, struct in6_prefixreq) /* get */ +#define SIOCDIFPREFIX_IN6 _IOW('i', 102, struct in6_prefixreq) /* del */ +#define SIOCAIFPREFIX_IN6 _IOW('i', 103, struct in6_rrenumreq) /* add */ +#define SIOCCIFPREFIX_IN6 _IOW('i', 104, \ struct in6_rrenumreq) /* change */ -#define SIOCSGIFPREFIX_IN6 _IOW('i', 105, \ +#define SIOCSGIFPREFIX_IN6 _IOW('i', 105, \ struct in6_rrenumreq) /* set global */ -#define SIOCGETSGCNT_IN6 _IOWR('u', 106, \ +#define SIOCGETSGCNT_IN6 _IOWR('u', 106, \ struct sioc_sg_req6) /* get s,g pkt cnt */ -#define SIOCGETMIFCNT_IN6 _IOWR('u', 107, \ +#define SIOCGETMIFCNT_IN6 _IOWR('u', 107, \ struct sioc_mif_req6) /* get pkt cnt per if */ -#define IN6_IFF_ANYCAST 0x01 /* anycast address */ -#define IN6_IFF_TENTATIVE 0x02 /* tentative address */ -#define IN6_IFF_DUPLICATED 0x04 /* DAD detected duplicate */ -#define IN6_IFF_DETACHED 0x08 /* may be detached from the link */ -#define IN6_IFF_DEPRECATED 0x10 /* deprecated address */ +#define IN6_IFF_ANYCAST 0x01 /* anycast address */ +#define IN6_IFF_TENTATIVE 0x02 /* tentative address */ +#define IN6_IFF_DUPLICATED 0x04 /* DAD detected duplicate */ +#define IN6_IFF_DETACHED 0x08 /* may be detached from the link */ +#define IN6_IFF_DEPRECATED 0x10 /* deprecated address */ /* do not input/output */ -#define IN6_IFF_NOTREADY (IN6_IFF_TENTATIVE|IN6_IFF_DUPLICATED) +#define IN6_IFF_NOTREADY (IN6_IFF_TENTATIVE|IN6_IFF_DUPLICATED) #ifdef _KERNEL -extern struct in6_ifaddr *in6_ifaddr; +#define IN6_ARE_SCOPE_CMP(a,b) ((a)-(b)) +#define IN6_ARE_SCOPE_EQUAL(a,b) ((a)==(b)) +#endif -extern struct in6_ifstat **in6_ifstat; -extern size_t in6_ifstatmax; -extern struct icmp6stat icmp6stat; -extern struct icmp6_ifstat **icmp6_ifstat; -extern size_t icmp6_ifstatmax; -#define in6_ifstat_inc(ifp, tag) \ +#ifdef _KERNEL +extern struct in6_ifaddr *in6_ifaddr; + +extern struct in6_ifstat **in6_ifstat; +extern size_t in6_ifstatmax; +extern struct icmp6stat icmp6stat; +extern struct icmp6_ifstat **icmp6_ifstat; +extern size_t icmp6_ifstatmax; +#define in6_ifstat_inc(ifp, tag) \ do { \ if ((ifp) && (ifp)->if_index <= if_index \ && (ifp)->if_index < in6_ifstatmax \ && in6_ifstat && in6_ifstat[(ifp)->if_index]) { \ in6_ifstat[(ifp)->if_index]->tag++; \ } \ } while (0) -extern struct ifqueue ip6intrq; /* IP6 packet input queue */ -extern struct in6_addr zeroin6_addr; -extern u_char inet6ctlerrmap[]; -extern u_long in6_maxmtu; +extern struct ifqueue ip6intrq; /* IP6 packet input queue */ +extern struct in6_addr zeroin6_addr; +extern u_char inet6ctlerrmap[]; +extern unsigned long in6_maxmtu; #ifdef MALLOC_DECLARE MALLOC_DECLARE(M_IPMADDR); -#endif /* MALLOC_DECLARE */ +#endif /* * Macro for finding the internet address structure (in6_ifaddr) corresponding * to a given interface (ifnet structure). */ -#define IFP_TO_IA6(ifp, ia) \ + +#define IFP_TO_IA6(ifp, ia) \ /* struct ifnet *ifp; */ \ /* struct in6_ifaddr *ia; */ \ do { \ struct ifaddr *ifa; \ - TAILQ_FOREACH(ifa, &(ifp)->if_addrlist, ifa_list) { \ + for (ifa = (ifp)->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next) { \ if (!ifa->ifa_addr) \ continue; \ if (ifa->ifa_addr->sa_family == AF_INET6) \ break; \ } \ (ia) = (struct in6_ifaddr *)ifa; \ } while (0) + #endif /* _KERNEL */ /* * Multi-cast membership entry. One for each group/ifp that a PCB * belongs to. */ struct in6_multi_mship { struct in6_multi *i6mm_maddr; /* Multicast address pointer */ - LIST_ENTRY(in6_multi_mship) i6mm_chain; /* multicast options chain */ + LIST_ENTRY(in6_multi_mship) i6mm_chain; /* multicast options chain */ }; struct in6_multi { - LIST_ENTRY(in6_multi) in6m_entry; /* list glue */ + LIST_ENTRY(in6_multi) in6m_entry; /* list glue */ struct in6_addr in6m_addr; /* IP6 multicast address */ struct ifnet *in6m_ifp; /* back pointer to ifnet */ struct ifmultiaddr *in6m_ifma; /* back pointer to ifmultiaddr */ u_int in6m_refcount; /* # membership claims by sockets */ u_int in6m_state; /* state of the membership */ u_int in6m_timer; /* MLD6 listener report timer */ }; #ifdef _KERNEL - -#ifdef SYSCTL_DECL -SYSCTL_DECL(_net_inet6_ip6); -#endif - extern LIST_HEAD(in6_multihead, in6_multi) in6_multihead; /* * Structure used by macros below to remember position when stepping through * all of eht in6_multi records. */ struct in6_multistep { struct in6_ifaddr *i_ia; struct in6_multi *i_in6m; }; /* * Macros for looking up the in6_multi record for a given IP6 multicast * address on a given interface. If no matching record is found, "in6m" * returns NLL. */ -#define IN6_LOOKUP_MULTI(addr, ifp, in6m) \ +#define IN6_LOOKUP_MULTI(addr, ifp, in6m) \ /* struct in6_addr addr; */ \ /* struct ifnet *ifp; */ \ /* struct in6_multi *in6m; */ \ do { \ register struct ifmultiaddr *ifma; \ for (ifma = (ifp)->if_multiaddrs.lh_first; ifma; \ ifma = ifma->ifma_link.le_next) { \ if (ifma->ifma_addr->sa_family == AF_INET6 \ && IN6_ARE_ADDR_EQUAL(&((struct sockaddr_in6 *)ifma->ifma_addr)->sin6_addr, \ &(addr))) \ break; \ } \ (in6m) = (struct in6_multi *)(ifma ? ifma->ifma_protospec : 0); \ } while(0) /* * Macro to step through all of the in6_multi records, one at a time. * The current position is remembered in "step", which the caller must * provide. IN6_FIRST_MULTI(), below, must be called to initialize "step" * and get the first record. Both macros return a NULL "in6m" when there * are no remaining records. */ -#define IN6_NEXT_MULTI(step, in6m) \ +#define IN6_NEXT_MULTI(step, in6m) \ /* struct in6_multistep step; */ \ /* struct in6_multi *in6m; */ \ do { \ if (((in6m) = (step).i_in6m) != NULL) \ - (step).i_in6m = LIST_NEXT((step).i_in6m, in6m_entry); \ + (step).i_in6m = (step).i_in6m->in6m_entry.le_next; \ } while(0) -#define IN6_FIRST_MULTI(step, in6m) \ +#define IN6_FIRST_MULTI(step, in6m) \ /* struct in6_multistep step; */ \ /* struct in6_multi *in6m */ \ do { \ - (step).i_in6m = LIST_FIRST(&in6_multihead); \ + (step).i_in6m = in6_multihead.lh_first; \ IN6_NEXT_MULTI((step), (in6m)); \ } while(0) -int in6_ifinit __P((struct ifnet *, +int in6_ifinit __P((struct ifnet *, struct in6_ifaddr *, struct sockaddr_in6 *, int)); -struct in6_multi *in6_addmulti __P((struct in6_addr *, struct ifnet *, +struct in6_multi *in6_addmulti __P((struct in6_addr *, struct ifnet *, int *)); -void in6_delmulti __P((struct in6_multi *)); -void in6_ifscrub __P((struct ifnet *, struct in6_ifaddr *)); -extern int in6_ifindex2scopeid __P((int)); -extern int in6_mask2len __P((struct in6_addr *)); -extern void in6_len2mask __P((struct in6_addr *, int)); -int in6_control __P((struct socket *, - u_long, caddr_t, struct ifnet *, struct proc *)); -void in6_savemkludge __P((struct in6_ifaddr *)); -void in6_setmaxmtu __P((void)); -void in6_restoremkludge __P((struct in6_ifaddr *, struct ifnet *)); -struct in6_ifaddr *in6ifa_ifpforlinklocal __P((struct ifnet *)); -struct in6_ifaddr *in6ifa_ifpwithaddr __P((struct ifnet *, +void in6_delmulti __P((struct in6_multi *)); +void in6_ifscrub __P((struct ifnet *, struct in6_ifaddr *)); +extern int in6_ifindex2scopeid __P((int)); +extern int in6_mask2len __P((struct in6_addr *)); +extern void in6_len2mask __P((struct in6_addr *, int)); +int in6_control __P((struct socket *, + u_long, caddr_t, struct ifnet *, struct proc *)); +void in6_purgeaddr __P((struct ifaddr *, struct ifnet *)); +void in6_savemkludge __P((struct in6_ifaddr *)); +void in6_setmaxmtu __P((void)); +void in6_restoremkludge __P((struct in6_ifaddr *, struct ifnet *)); +void in6_purgemkludge __P((struct ifnet *)); +struct in6_ifaddr *in6ifa_ifpforlinklocal __P((struct ifnet *, int)); +struct in6_ifaddr *in6ifa_ifpwithaddr __P((struct ifnet *, struct in6_addr *)); char *ip6_sprintf __P((struct in6_addr *)); -int in6_matchlen __P((struct in6_addr *, struct in6_addr *)); -int in6_are_prefix_equal __P((struct in6_addr *p1, struct in6_addr *p2, +int in6_addr2scopeid __P((struct ifnet *, struct in6_addr *)); +int in6_matchlen __P((struct in6_addr *, struct in6_addr *)); +int in6_are_prefix_equal __P((struct in6_addr *p1, struct in6_addr *p2, int len)); -void in6_prefixlen2mask __P((struct in6_addr *maskp, int len)); -int in6_prefix_ioctl __P((struct socket *so, u_long cmd, caddr_t data, +void in6_prefixlen2mask __P((struct in6_addr *maskp, int len)); +int in6_prefix_ioctl __P((struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp)); -int in6_prefix_add_ifid __P((int iilen, struct in6_ifaddr *ia)); -void in6_prefix_remove_ifid __P((int iilen, struct in6_ifaddr *ia)); +int in6_prefix_add_ifid __P((int iilen, struct in6_ifaddr *ia)); +void in6_prefix_remove_ifid __P((int iilen, struct in6_ifaddr *ia)); +void in6_purgeprefix __P((struct ifnet *)); #endif /* _KERNEL */ #endif /* _NETINET6_IN6_VAR_H_ */ Index: head/sys/netinet6/ip6.h =================================================================== --- head/sys/netinet6/ip6.h (revision 62586) +++ head/sys/netinet6/ip6.h (revision 62587) @@ -1,253 +1,4 @@ -/* - * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the project nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * $FreeBSD$ - */ +/* $FreeBSD$ */ +/* $KAME: ip6.h,v 1.7 2000/03/25 07:23:36 sumikawa Exp $ */ -/* - * Copyright (c) 1982, 1986, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)ip.h 8.1 (Berkeley) 6/10/93 - */ - -#ifndef _NETINET6_IPV6_H_ -#define _NETINET6_IPV6_H_ - -#if !defined(_KERNEL) && !defined(__KAME_NETINET_IP6_H_INCLUDED_) -#error "do not include netinet6/ip6.h directly, include netinet/ip6.h" -#endif - -/* - * Definition for internet protocol version 6. - * RFC 2460 - */ - -struct ip6_hdr { - union { - struct ip6_hdrctl { - u_int32_t ip6_un1_flow; /* 4 bits version, - * 8 bits traffic - * class, - * 20 bits flow-ID */ - u_int16_t ip6_un1_plen; /* payload length */ - u_int8_t ip6_un1_nxt; /* next header */ - u_int8_t ip6_un1_hlim; /* hop limit */ - } ip6_un1; - u_int8_t ip6_un2_vfc; /* 4 bits version, - * top 4 bits trafic class */ - } ip6_ctlun; - struct in6_addr ip6_src; /* source address */ - struct in6_addr ip6_dst; /* destination address */ -}; - -#define ip6_vfc ip6_ctlun.ip6_un2_vfc -#define ip6_flow ip6_ctlun.ip6_un1.ip6_un1_flow -#define ip6_plen ip6_ctlun.ip6_un1.ip6_un1_plen -#define ip6_nxt ip6_ctlun.ip6_un1.ip6_un1_nxt -#define ip6_hlim ip6_ctlun.ip6_un1.ip6_un1_hlim -#define ip6_hops ip6_ctlun.ip6_un1.ip6_un1_hlim - -#define IPV6_VERSION 0x60 -#define IPV6_VERSION_MASK 0xf0 - -#if BYTE_ORDER == BIG_ENDIAN -#define IPV6_FLOWINFO_MASK 0x0fffffff /* flow info (28 bits) */ -#define IPV6_FLOWLABEL_MASK 0x000fffff /* flow label (20 bits) */ -#endif /* BIG_ENDIAN */ -#if BYTE_ORDER == LITTLE_ENDIAN -#define IPV6_FLOWINFO_MASK 0xffffff0f /* flow info (28 bits) */ -#define IPV6_FLOWLABEL_MASK 0xffff0f00 /* flow label (20 bits) */ -#endif /* LITTLE_ENDIAN */ -/* ECN bits proposed by Sally Floyd */ -#define IP6TOS_CE 0x01 /* congestion experienced */ -#define IP6TOS_ECT 0x02 /* ECN-capable transport */ - -/* - * Extension Headers - */ - -struct ip6_ext { - u_char ip6e_nxt; - u_char ip6e_len; -}; - -/* Hop-by-Hop options header */ -/* XXX should we pad it to force alignment on an 8-byte boundary? */ -struct ip6_hbh { - u_int8_t ip6h_nxt; /* next header */ - u_int8_t ip6h_len; /* length in units of 8 octets */ - /* followed by options */ -}; - -/* Destination options header */ -/* XXX should we pad it to force alignment on an 8-byte boundary? */ -struct ip6_dest { - u_int8_t ip6d_nxt; /* next header */ - u_int8_t ip6d_len; /* length in units of 8 octets */ - /* followed by options */ -}; - -/* Option types and related macros */ -#define IP6OPT_PAD1 0x00 /* 00 0 00000 */ -#define IP6OPT_PADN 0x01 /* 00 0 00001 */ -#define IP6OPT_JUMBO 0xC2 /* 11 0 00010 = 194 */ -#define IP6OPT_JUMBO_LEN 6 -#define IP6OPT_RTALERT 0x05 /* 00 0 00101 */ -#define IP6OPT_RTALERT_LEN 4 -#define IP6OPT_RTALERT_MLD 0 /* Datagram contains an MLD message */ -#define IP6OPT_RTALERT_RSVP 1 /* Datagram contains an RSVP message */ -#define IP6OPT_RTALERT_ACTNET 2 /* contains an Active Networks msg */ -#define IP6OPT_MINLEN 2 - -#define IP6OPT_TYPE(o) ((o) & 0xC0) -#define IP6OPT_TYPE_SKIP 0x00 -#define IP6OPT_TYPE_DISCARD 0x40 -#define IP6OPT_TYPE_FORCEICMP 0x80 -#define IP6OPT_TYPE_ICMP 0xC0 - -#define IP6OPT_MUTABLE 0x20 - -/* Routing header */ -struct ip6_rthdr { - u_int8_t ip6r_nxt; /* next header */ - u_int8_t ip6r_len; /* length in units of 8 octets */ - u_int8_t ip6r_type; /* routing type */ - u_int8_t ip6r_segleft; /* segments left */ - /* followed by routing type specific data */ -}; - -/* Type 0 Routing header */ -struct ip6_rthdr0 { - u_int8_t ip6r0_nxt; /* next header */ - u_int8_t ip6r0_len; /* length in units of 8 octets */ - u_int8_t ip6r0_type; /* always zero */ - u_int8_t ip6r0_segleft; /* segments left */ - u_int8_t ip6r0_reserved; /* reserved field */ - u_int8_t ip6r0_slmap[3]; /* strict/loose bit map */ - struct in6_addr ip6r0_addr[1]; /* up to 23 addresses */ -}; - -/* Fragment header */ -struct ip6_frag { - u_int8_t ip6f_nxt; /* next header */ - u_int8_t ip6f_reserved; /* reserved field */ - u_int16_t ip6f_offlg; /* offset, reserved, and flag */ - u_int32_t ip6f_ident; /* identification */ -}; - -#if BYTE_ORDER == BIG_ENDIAN -#define IP6F_OFF_MASK 0xfff8 /* mask out offset from _offlg */ -#define IP6F_RESERVED_MASK 0x0006 /* reserved bits in ip6f_offlg */ -#define IP6F_MORE_FRAG 0x0001 /* more-fragments flag */ -#else /* BYTE_ORDER == LITTLE_ENDIAN */ -#define IP6F_OFF_MASK 0xf8ff /* mask out offset from _offlg */ -#define IP6F_RESERVED_MASK 0x0600 /* reserved bits in ip6f_offlg */ -#define IP6F_MORE_FRAG 0x0100 /* more-fragments flag */ -#endif /* BYTE_ORDER == LITTLE_ENDIAN */ - -/* - * Internet implementation parameters. - */ -#define IPV6_MAXHLIM 255 /* maximun hoplimit */ -#define IPV6_DEFHLIM 64 /* default hlim */ -#define IPV6_FRAGTTL 120 /* ttl for fragment packets, in slowtimo tick */ -#define IPV6_HLIMDEC 1 /* subtracted when forwaeding */ - -#define IPV6_MMTU 1280 /* minimal MTU and reassembly. 1024 + 256 */ -#define IPV6_MAXPACKET 65535 /* ip6 max packet size without Jumbo payload*/ - -/* - * IP6_EXTHDR_CHECK ensures that region between the IP6 header and the - * target header (including IPv6 itself, extension headers and - * TCP/UDP/ICMP6 headers) are continuous. KAME requires drivers - * to store incoming data into one internal mbuf or one or more external - * mbufs(never into two or more internal mbufs). Thus, the third case is - * supposed to never be matched but is prepared just in case. - */ - -#define IP6_EXTHDR_CHECK(m, off, hlen, ret) \ -do { \ - if ((m)->m_next != NULL) { \ - if (((m)->m_flags & M_LOOP) && \ - ((m)->m_len < (off) + (hlen)) && \ - (((m) = m_pullup((m), (off) + (hlen))) == NULL)) { \ - ip6stat.ip6s_exthdrtoolong++; \ - return ret; \ - } else if ((m)->m_flags & M_EXT) { \ - if ((m)->m_len < (off) + (hlen)) { \ - ip6stat.ip6s_exthdrtoolong++; \ - m_freem(m); \ - return ret; \ - } \ - } else { \ - if ((m)->m_len < (off) + (hlen)) { \ - ip6stat.ip6s_exthdrtoolong++; \ - m_freem(m); \ - return ret; \ - } \ - } \ - } else { \ - if ((m)->m_len < (off) + (hlen)) { \ - ip6stat.ip6s_tooshort++; \ - in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_truncated); \ - m_freem(m); \ - return ret; \ - } \ - } \ -} while (0) - -#endif /* not _NETINET_IPV6_H_ */ +#error "netinet6/ip6.h is obsolete. use netinet/ip6.h" Index: head/sys/netinet6/ip6_ecn.h =================================================================== --- head/sys/netinet6/ip6_ecn.h (revision 62586) +++ head/sys/netinet6/ip6_ecn.h (revision 62587) @@ -1,43 +1,41 @@ +/* $FreeBSD$ */ +/* $KAME: ip_ecn.h,v 1.5 2000/03/27 04:58:38 sumikawa Exp $ */ + /* * Copyright (C) 1999 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: ip_ecn.h,v 1.2 1999/08/19 12:57:44 itojun Exp $ - * $FreeBSD$ */ /* * ECN consideration on tunnel ingress/egress operation. * http://www.aciri.org/floyd/papers/draft-ipsec-ecn-00.txt */ #ifdef _KERNEL extern void ip6_ecn_ingress __P((int, u_int32_t *, u_int32_t *)); extern void ip6_ecn_egress __P((int, u_int32_t *, u_int32_t *)); #endif - - - Index: head/sys/netinet6/ip6_forward.c =================================================================== --- head/sys/netinet6/ip6_forward.c (revision 62586) +++ head/sys/netinet6/ip6_forward.c (revision 62587) @@ -1,379 +1,542 @@ +/* $FreeBSD$ */ +/* $KAME: ip6_forward.c,v 1.39 2000/07/03 13:23:28 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ +#include "opt_ip6fw.h" +#include "opt_inet.h" +#include "opt_inet6.h" #include "opt_ipsec.h" #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include +#include #include -#include +#include #include #ifdef IPSEC_IPV6FWD #include -#include #include -#ifdef IPSEC_DEBUG -#include -#else -#define KEYDEBUG(lev,arg) -#endif #endif /* IPSEC_IPV6FWD */ #ifdef IPV6FIREWALL #include #endif #include struct route_in6 ip6_forward_rt; /* * Forward a packet. If some error occurs return the sender * an icmp packet. Note we can't always generate a meaningful * icmp message because icmp doesn't have a large enough repertoire * of codes and types. * * If not forwarding, just drop the packet. This could be confusing * if ipforwarding was zero but some routing protocol was advancing * us as a gateway to somewhere. However, we must let the routing * protocol deal with that. * */ void ip6_forward(m, srcrt) struct mbuf *m; int srcrt; { struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); register struct sockaddr_in6 *dst; register struct rtentry *rt; int error, type = 0, code = 0; - struct mbuf *mcopy; + struct mbuf *mcopy = NULL; + struct ifnet *origifp; /* maybe unnecessary */ #ifdef IPSEC_IPV6FWD struct secpolicy *sp = NULL; #endif #ifdef IPSEC_IPV6FWD /* * Check AH/ESP integrity. */ /* * Don't increment ip6s_cantforward because this is the check * before forwarding packet actually. */ if (ipsec6_in_reject(m, NULL)) { ipsec6stat.in_polvio++; m_freem(m); return; } #endif /*IPSEC_IPV6FWD*/ - if (m->m_flags & (M_BCAST|M_MCAST) || - in6_canforward(&ip6->ip6_src, &ip6->ip6_dst) == 0) { + if ((m->m_flags & (M_BCAST|M_MCAST)) != 0 || + IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { ip6stat.ip6s_cantforward++; - ip6stat.ip6s_badscope++; /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard) */ if (ip6_log_time + ip6_log_interval < time_second) { - char addr[INET6_ADDRSTRLEN]; ip6_log_time = time_second; - strncpy(addr, ip6_sprintf(&ip6->ip6_src), sizeof(addr)); log(LOG_DEBUG, "cannot forward " "from %s to %s nxt %d received on %s\n", - addr, ip6_sprintf(&ip6->ip6_dst), + ip6_sprintf(&ip6->ip6_src), + ip6_sprintf(&ip6->ip6_dst), ip6->ip6_nxt, if_name(m->m_pkthdr.rcvif)); } m_freem(m); return; } if (ip6->ip6_hlim <= IPV6_HLIMDEC) { /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard) */ icmp6_error(m, ICMP6_TIME_EXCEEDED, ICMP6_TIME_EXCEED_TRANSIT, 0); return; } ip6->ip6_hlim -= IPV6_HLIMDEC; + /* + * Save at most ICMPV6_PLD_MAXLEN (= the min IPv6 MTU - + * size of IPv6 + ICMPv6 headers) bytes of the packet in case + * we need to generate an ICMP6 message to the src. + * Thanks to M_EXT, in most cases copy will not occur. + * + * It is important to save it before IPsec processing as IPsec + * processing may modify the mbuf. + */ + mcopy = m_copy(m, 0, imin(m->m_pkthdr.len, ICMPV6_PLD_MAXLEN)); + #ifdef IPSEC_IPV6FWD /* get a security policy for this packet */ sp = ipsec6_getpolicybyaddr(m, IPSEC_DIR_OUTBOUND, 0, &error); if (sp == NULL) { ipsec6stat.out_inval++; ip6stat.ip6s_cantforward++; - /* XXX: any icmp ? */ + if (mcopy) { +#if 0 + /* XXX: what icmp ? */ +#else + m_freem(mcopy); +#endif + } m_freem(m); return; } error = 0; /* check policy */ switch (sp->policy) { case IPSEC_POLICY_DISCARD: /* * This packet is just discarded. */ ipsec6stat.out_polvio++; ip6stat.ip6s_cantforward++; key_freesp(sp); - /* XXX: any icmp ? */ + if (mcopy) { +#if 0 + /* XXX: what icmp ? */ +#else + m_freem(mcopy); +#endif + } m_freem(m); return; case IPSEC_POLICY_BYPASS: case IPSEC_POLICY_NONE: /* no need to do IPsec. */ key_freesp(sp); goto skip_ipsec; - + case IPSEC_POLICY_IPSEC: if (sp->req == NULL) { /* XXX should be panic ? */ printf("ip6_forward: No IPsec request specified.\n"); ip6stat.ip6s_cantforward++; key_freesp(sp); - /* XXX: any icmp ? */ + if (mcopy) { +#if 0 + /* XXX: what icmp ? */ +#else + m_freem(mcopy); +#endif + } m_freem(m); return; } /* do IPsec */ break; case IPSEC_POLICY_ENTRUST: default: /* should be panic ?? */ printf("ip6_forward: Invalid policy found. %d\n", sp->policy); key_freesp(sp); goto skip_ipsec; } { struct ipsec_output_state state; /* * All the extension headers will become inaccessible * (since they can be encrypted). * Don't panic, we need no more updates to extension headers * on inner IPv6 packet (since they are now encapsulated). * * IPv6 [ESP|AH] IPv6 [extension headers] payload */ bzero(&state, sizeof(state)); state.m = m; state.ro = NULL; /* update at ipsec6_output_tunnel() */ state.dst = NULL; /* update at ipsec6_output_tunnel() */ error = ipsec6_output_tunnel(&state, sp, 0); m = state.m; - /* XXX allocate a route (ro, dst) again later */ +#if 0 /* XXX allocate a route (ro, dst) again later */ + ro = (struct route_in6 *)state.ro; + dst = (struct sockaddr_in6 *)state.dst; +#endif key_freesp(sp); if (error) { /* mbuf is already reclaimed in ipsec6_output_tunnel. */ switch (error) { case EHOSTUNREACH: case ENETUNREACH: case EMSGSIZE: case ENOBUFS: case ENOMEM: break; default: printf("ip6_output (ipsec): error code %d\n", error); /*fall through*/ case ENOENT: /* don't show these error codes to the user */ break; } ip6stat.ip6s_cantforward++; - /* XXX: any icmp ? */ + if (mcopy) { +#if 0 + /* XXX: what icmp ? */ +#else + m_freem(mcopy); +#endif + } m_freem(m); return; } } skip_ipsec: #endif /* IPSEC_IPV6FWD */ dst = &ip6_forward_rt.ro_dst; if (!srcrt) { /* * ip6_forward_rt.ro_dst.sin6_addr is equal to ip6->ip6_dst */ if (ip6_forward_rt.ro_rt == 0 || (ip6_forward_rt.ro_rt->rt_flags & RTF_UP) == 0) { if (ip6_forward_rt.ro_rt) { RTFREE(ip6_forward_rt.ro_rt); ip6_forward_rt.ro_rt = 0; } /* this probably fails but give it a try again */ rtalloc_ign((struct route *)&ip6_forward_rt, RTF_PRCLONING); } - + if (ip6_forward_rt.ro_rt == 0) { ip6stat.ip6s_noroute++; /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_noroute) */ - icmp6_error(m, ICMP6_DST_UNREACH, - ICMP6_DST_UNREACH_NOROUTE, 0); + if (mcopy) { + icmp6_error(mcopy, ICMP6_DST_UNREACH, + ICMP6_DST_UNREACH_NOROUTE, 0); + } + m_freem(m); return; } } else if ((rt = ip6_forward_rt.ro_rt) == 0 || !IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &dst->sin6_addr)) { if (ip6_forward_rt.ro_rt) { RTFREE(ip6_forward_rt.ro_rt); ip6_forward_rt.ro_rt = 0; } bzero(dst, sizeof(*dst)); dst->sin6_len = sizeof(struct sockaddr_in6); dst->sin6_family = AF_INET6; dst->sin6_addr = ip6->ip6_dst; rtalloc_ign((struct route *)&ip6_forward_rt, RTF_PRCLONING); if (ip6_forward_rt.ro_rt == 0) { ip6stat.ip6s_noroute++; /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_noroute) */ - icmp6_error(m, ICMP6_DST_UNREACH, - ICMP6_DST_UNREACH_NOROUTE, 0); + if (mcopy) { + icmp6_error(mcopy, ICMP6_DST_UNREACH, + ICMP6_DST_UNREACH_NOROUTE, 0); + } + m_freem(m); return; } } rt = ip6_forward_rt.ro_rt; - if (m->m_pkthdr.len > rt->rt_ifp->if_mtu){ + + /* + * Scope check: if a packet can't be delivered to its destination + * for the reason that the destination is beyond the scope of the + * source address, discard the packet and return an icmp6 destination + * unreachable error with Code 2 (beyond scope of source address). + * [draft-ietf-ipngwg-icmp-v3-00.txt, Section 3.1] + */ + if (in6_addr2scopeid(m->m_pkthdr.rcvif, &ip6->ip6_src) != + in6_addr2scopeid(rt->rt_ifp, &ip6->ip6_src)) { + ip6stat.ip6s_cantforward++; + ip6stat.ip6s_badscope++; + in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard); + + if (ip6_log_time + ip6_log_interval < time_second) { + ip6_log_time = time_second; + log(LOG_DEBUG, + "cannot forward " + "src %s, dst %s, nxt %d, rcvif %s, outif %s\n", + ip6_sprintf(&ip6->ip6_src), + ip6_sprintf(&ip6->ip6_dst), + ip6->ip6_nxt, + if_name(m->m_pkthdr.rcvif), if_name(rt->rt_ifp)); + } + if (mcopy) + icmp6_error(mcopy, ICMP6_DST_UNREACH, + ICMP6_DST_UNREACH_BEYONDSCOPE, 0); + m_freem(m); + return; + } + + if (m->m_pkthdr.len > rt->rt_ifp->if_mtu) { in6_ifstat_inc(rt->rt_ifp, ifs6_in_toobig); - icmp6_error(m, ICMP6_PACKET_TOO_BIG, 0, rt->rt_ifp->if_mtu); + if (mcopy) { + u_long mtu; +#ifdef IPSEC_IPV6FWD + struct secpolicy *sp; + int ipsecerror; + size_t ipsechdrsiz; +#endif + + mtu = rt->rt_ifp->if_mtu; +#ifdef IPSEC_IPV6FWD + /* + * When we do IPsec tunnel ingress, we need to play + * with if_mtu value (decrement IPsec header size + * from mtu value). The code is much simpler than v4 + * case, as we have the outgoing interface for + * encapsulated packet as "rt->rt_ifp". + */ + sp = ipsec6_getpolicybyaddr(mcopy, IPSEC_DIR_OUTBOUND, + IP_FORWARDING, &ipsecerror); + if (sp) { + ipsechdrsiz = ipsec6_hdrsiz(mcopy, + IPSEC_DIR_OUTBOUND, NULL); + if (ipsechdrsiz < mtu) + mtu -= ipsechdrsiz; + } + + /* + * if mtu becomes less than minimum MTU, + * tell minimum MTU (and I'll need to fragment it). + */ + if (mtu < IPV6_MMTU) + mtu = IPV6_MMTU; +#endif + icmp6_error(mcopy, ICMP6_PACKET_TOO_BIG, 0, mtu); + } + m_freem(m); return; } if (rt->rt_flags & RTF_GATEWAY) dst = (struct sockaddr_in6 *)rt->rt_gateway; - /* - * Save at most 528 bytes of the packet in case - * we need to generate an ICMP6 message to the src. - * Thanks to M_EXT, in most cases copy will not occur. - */ - mcopy = m_copy(m, 0, imin(m->m_pkthdr.len, ICMPV6_PLD_MAXLEN)); /* * If we are to forward the packet using the same interface * as one we got the packet from, perhaps we should send a redirect * to sender to shortcut a hop. * Only send redirect if source is sending directly to us, * and if packet was not source routed (or has any options). * Also, don't send redirect if forwarding using a route * modified by a redirect. */ if (rt->rt_ifp == m->m_pkthdr.rcvif && !srcrt && (rt->rt_flags & (RTF_DYNAMIC|RTF_MODIFIED)) == 0) type = ND_REDIRECT; #ifdef IPV6FIREWALL /* * Check with the firewall... */ if (ip6_fw_chk_ptr) { u_short port = 0; /* If ipfw says divert, we have to just drop packet */ if ((*ip6_fw_chk_ptr)(&ip6, rt->rt_ifp, &port, &m)) { m_freem(m); goto freecopy; } if (!m) goto freecopy; } #endif - error = nd6_output(rt->rt_ifp, m, dst, rt); + /* + * Fake scoped addresses. Note that even link-local source or + * destinaion can appear, if the originating node just sends the + * packet to us (without address resolution for the destination). + * Since both icmp6_error and icmp6_redirect_output fill the embedded + * link identifiers, we can do this stuff after make a copy for + * returning error. + */ + if ((rt->rt_ifp->if_flags & IFF_LOOPBACK) != 0) { + /* + * See corresponding comments in ip6_output. + * XXX: but is it possible that ip6_forward() sends a packet + * to a loopback interface? I don't think so, and thus + * I bark here. (jinmei@kame.net) + * XXX: it is common to route invalid packets to loopback. + * also, the codepath will be visited on use of ::1 in + * rthdr. (itojun) + */ +#if 1 + if (0) +#else + if ((rt->rt_flags & (RTF_BLACKHOLE|RTF_REJECT)) == 0) +#endif + { + printf("ip6_forward: outgoing interface is loopback. " + "src %s, dst %s, nxt %d, rcvif %s, outif %s\n", + ip6_sprintf(&ip6->ip6_src), + ip6_sprintf(&ip6->ip6_dst), + ip6->ip6_nxt, if_name(m->m_pkthdr.rcvif), + if_name(rt->rt_ifp)); + } + + if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) + origifp = ifindex2ifnet[ntohs(ip6->ip6_src.s6_addr16[1])]; + else if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) + origifp = ifindex2ifnet[ntohs(ip6->ip6_dst.s6_addr16[1])]; + else + origifp = rt->rt_ifp; + } + else + origifp = rt->rt_ifp; +#ifndef FAKE_LOOPBACK_IF + if ((rt->rt_ifp->if_flags & IFF_LOOPBACK) == 0) +#else + if (1) +#endif + { + if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) + ip6->ip6_src.s6_addr16[1] = 0; + if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) + ip6->ip6_dst.s6_addr16[1] = 0; + } + +#ifdef OLDIP6OUTPUT + error = (*rt->rt_ifp->if_output)(rt->rt_ifp, m, + (struct sockaddr *)dst, + ip6_forward_rt.ro_rt); +#else + error = nd6_output(rt->rt_ifp, origifp, m, dst, rt); +#endif if (error) { in6_ifstat_inc(rt->rt_ifp, ifs6_out_discard); ip6stat.ip6s_cantforward++; } else { ip6stat.ip6s_forward++; in6_ifstat_inc(rt->rt_ifp, ifs6_out_forward); if (type) ip6stat.ip6s_redirectsent++; else { if (mcopy) goto freecopy; } } if (mcopy == NULL) return; switch (error) { case 0: +#if 1 if (type == ND_REDIRECT) { icmp6_redirect_output(mcopy, rt); return; } +#endif goto freecopy; case EMSGSIZE: /* xxx MTU is constant in PPP? */ goto freecopy; case ENOBUFS: /* Tell source to slow down like source quench in IP? */ goto freecopy; case ENETUNREACH: /* shouldn't happen, checked above */ case EHOSTUNREACH: case ENETDOWN: case EHOSTDOWN: default: type = ICMP6_DST_UNREACH; code = ICMP6_DST_UNREACH_ADDR; break; } icmp6_error(mcopy, type, code, 0); return; freecopy: m_freem(mcopy); return; } Index: head/sys/netinet6/ip6_fw.c =================================================================== --- head/sys/netinet6/ip6_fw.c (revision 62586) +++ head/sys/netinet6/ip6_fw.c (revision 62587) @@ -1,1173 +1,1186 @@ +/* $FreeBSD$ */ +/* $KAME: ip6_fw.c,v 1.15 2000/07/02 14:17:37 itojun Exp $ */ + /* * Copyright (c) 1993 Daniel Boulet * Copyright (c) 1994 Ugen J.S.Antsilevich * Copyright (c) 1996 Alex Nash * * Redistribution and use in source forms, with and without modification, * are permitted provided that this entire comment appears intact. * * Redistribution in binary form may occur without any restrictions. * Obviously, it would be nice if you gave credit where credit is due * but requiring it would be too onerous. * * This software is provided ``AS IS'' without any warranties of any kind. - * - * $Id: ip6_fw.c,v 1.7 1999/08/31 12:25:57 shin Exp $ - * $FreeBSD$ */ /* * Implement IPv6 packet firewall */ #include "opt_ip6fw.h" +#include "opt_inet.h" +#include "opt_inet6.h" +#ifdef IP6DIVERT +#error "NOT SUPPORTED IPV6 DIVERT" +#endif +#ifdef IP6FW_DIVERT_RESTART +#error "NOT SUPPORTED IPV6 DIVERT" +#endif + #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include + +#include +#include #include -#include -#include +#include + +#include + #include #include #include #include #include #include #include #include #include MALLOC_DEFINE(M_IP6FW, "Ip6Fw/Ip6Acct", "Ip6Fw/Ip6Acct chain's"); static int fw6_debug = 1; #ifdef IPV6FIREWALL_VERBOSE static int fw6_verbose = 1; #else static int fw6_verbose = 0; #endif #ifdef IPV6FIREWALL_VERBOSE_LIMIT static int fw6_verbose_limit = IPV6FIREWALL_VERBOSE_LIMIT; #else static int fw6_verbose_limit = 0; #endif LIST_HEAD (ip6_fw_head, ip6_fw_chain) ip6_fw_chain; #ifdef SYSCTL_NODE SYSCTL_DECL(_net_inet6_ip6); SYSCTL_NODE(_net_inet6_ip6, OID_AUTO, fw, CTLFLAG_RW, 0, "Firewall"); SYSCTL_INT(_net_inet6_ip6_fw, OID_AUTO, debug, CTLFLAG_RW, &fw6_debug, 0, ""); SYSCTL_INT(_net_inet6_ip6_fw, OID_AUTO, verbose, CTLFLAG_RW, &fw6_verbose, 0, ""); SYSCTL_INT(_net_inet6_ip6_fw, OID_AUTO, verbose_limit, CTLFLAG_RW, &fw6_verbose_limit, 0, ""); #endif -#define dprintf(a) if (!fw6_debug); else printf a +#define dprintf(a) if (!fw6_debug); else printf a -#define print_ip6(a) printf("[%s]", ip6_sprintf(a)) +#define print_ip6(a) printf("[%s]", ip6_sprintf(a)) -#define dprint_ip6(a) if (!fw6_debug); else print_ip6(a) +#define dprint_ip6(a) if (!fw6_debug); else print_ip6(a) static int add_entry6 __P((struct ip6_fw_head *chainptr, struct ip6_fw *frwl)); static int del_entry6 __P((struct ip6_fw_head *chainptr, u_short number)); static int zero_entry6 __P((struct mbuf *m)); static struct ip6_fw *check_ip6fw_struct __P((struct ip6_fw *m)); static struct ip6_fw *check_ip6fw_mbuf __P((struct mbuf *fw)); static int ip6opts_match __P((struct ip6_hdr **ip6, struct ip6_fw *f, struct mbuf **m, int *off, int *nxt, u_short *offset)); static int port_match6 __P((u_short *portptr, int nports, u_short port, int range_flag)); static int tcp6flg_match __P((struct tcphdr *tcp6, struct ip6_fw *f)); static int icmp6type_match __P((struct icmp6_hdr * icmp, struct ip6_fw * f)); static void ip6fw_report __P((struct ip6_fw *f, struct ip6_hdr *ip6, struct ifnet *rif, struct ifnet *oif, int off, int nxt)); static int ip6_fw_chk __P((struct ip6_hdr **pip6, struct ifnet *oif, u_int16_t *cookie, struct mbuf **m)); static int ip6_fw_ctl __P((int stage, struct mbuf **mm)); static char err_prefix[] = "ip6_fw_ctl:"; /* * Returns 1 if the port is matched by the vector, 0 otherwise */ static __inline int port_match6(u_short *portptr, int nports, u_short port, int range_flag) { if (!nports) return 1; if (range_flag) { if (portptr[0] <= port && port <= portptr[1]) { return 1; } nports -= 2; portptr += 2; } while (nports-- > 0) { if (*portptr++ == port) { return 1; } } return 0; } static int tcp6flg_match(struct tcphdr *tcp6, struct ip6_fw *f) { u_char flg_set, flg_clr; - + if ((f->fw_tcpf & IPV6_FW_TCPF_ESTAB) && (tcp6->th_flags & (IPV6_FW_TCPF_RST | IPV6_FW_TCPF_ACK))) return 1; flg_set = tcp6->th_flags & f->fw_tcpf; flg_clr = tcp6->th_flags & f->fw_tcpnf; if (flg_set != f->fw_tcpf) return 0; if (flg_clr) return 0; return 1; } static int icmp6type_match(struct icmp6_hdr *icmp6, struct ip6_fw *f) { int type; if (!(f->fw_flg & IPV6_FW_F_ICMPBIT)) return(1); type = icmp6->icmp6_type; /* check for matching type in the bitmap */ if (type < IPV6_FW_ICMPTYPES_DIM * sizeof(unsigned) * 8 && (f->fw_icmp6types[type / (sizeof(unsigned) * 8)] & (1U << (type % (8 * sizeof(unsigned)))))) return(1); return(0); /* no match */ } static int is_icmp6_query(struct ip6_hdr *ip6, int off) { const struct icmp6_hdr *icmp6; int icmp6_type; icmp6 = (struct icmp6_hdr *)((caddr_t)ip6 + off); icmp6_type = icmp6->icmp6_type; if (icmp6_type == ICMP6_ECHO_REQUEST || icmp6_type == ICMP6_MEMBERSHIP_QUERY || icmp6_type == ICMP6_WRUREQUEST || icmp6_type == ICMP6_FQDN_QUERY || icmp6_type == ICMP6_NI_QUERY) return(1); return(0); } static int ip6opts_match(struct ip6_hdr **pip6, struct ip6_fw *f, struct mbuf **m, int *off, int *nxt, u_short *offset) { int len; struct ip6_hdr *ip6 = *pip6; struct ip6_ext *ip6e; u_char opts, nopts, nopts_sve; opts = f->fw_ip6opt; nopts = nopts_sve = f->fw_ip6nopt; *nxt = ip6->ip6_nxt; *off = sizeof(struct ip6_hdr); len = ntohs(ip6->ip6_plen) + sizeof(struct ip6_hdr); while (*off < len) { ip6e = (struct ip6_ext *)((caddr_t) ip6 + *off); if ((*m)->m_len < *off + sizeof(*ip6e)) goto opts_check; /* XXX */ switch(*nxt) { case IPPROTO_FRAGMENT: if ((*m)->m_len < *off + sizeof(struct ip6_frag)) { struct ip6_frag *ip6f; ip6f = (struct ip6_frag *) ((caddr_t)ip6 + *off); *offset = ip6f->ip6f_offlg | IP6F_OFF_MASK; } opts &= ~IPV6_FW_IP6OPT_FRAG; nopts &= ~IPV6_FW_IP6OPT_FRAG; *off += sizeof(struct ip6_frag); break; case IPPROTO_AH: opts &= ~IPV6_FW_IP6OPT_AH; nopts &= ~IPV6_FW_IP6OPT_AH; *off += (ip6e->ip6e_len + 2) << 2; break; default: switch (*nxt) { case IPPROTO_HOPOPTS: opts &= ~IPV6_FW_IP6OPT_HOPOPT; nopts &= ~IPV6_FW_IP6OPT_HOPOPT; break; case IPPROTO_ROUTING: opts &= ~IPV6_FW_IP6OPT_ROUTE; nopts &= ~IPV6_FW_IP6OPT_ROUTE; break; case IPPROTO_ESP: opts &= ~IPV6_FW_IP6OPT_ESP; nopts &= ~IPV6_FW_IP6OPT_ESP; break; case IPPROTO_NONE: opts &= ~IPV6_FW_IP6OPT_NONXT; nopts &= ~IPV6_FW_IP6OPT_NONXT; goto opts_check; break; case IPPROTO_DSTOPTS: opts &= ~IPV6_FW_IP6OPT_OPTS; nopts &= ~IPV6_FW_IP6OPT_OPTS; break; default: goto opts_check; break; } *off += (ip6e->ip6e_len + 1) << 3; break; } *nxt = ip6e->ip6e_nxt; } opts_check: if (f->fw_ip6opt == f->fw_ip6nopt) /* XXX */ return 1; if (opts == 0 && nopts == nopts_sve) return 1; else return 0; } static __inline int iface_match(struct ifnet *ifp, union ip6_fw_if *ifu, int byname) { /* Check by name or by IP address */ if (byname) { /* Check unit number (-1 is wildcard) */ if (ifu->fu_via_if.unit != -1 && ifp->if_unit != ifu->fu_via_if.unit) return(0); /* Check name */ if (strncmp(ifp->if_name, ifu->fu_via_if.name, FW_IFNLEN)) return(0); return(1); } else if (!IN6_IS_ADDR_UNSPECIFIED(&ifu->fu_via_ip6)) { /* Zero == wildcard */ struct ifaddr *ia; for (ia = ifp->if_addrlist.tqh_first; ia; ia = ia->ifa_list.tqe_next) { if (ia->ifa_addr == NULL) continue; if (ia->ifa_addr->sa_family != AF_INET6) continue; if (!IN6_ARE_ADDR_EQUAL(&ifu->fu_via_ip6, &(((struct sockaddr_in6 *) (ia->ifa_addr))->sin6_addr))) continue; return(1); } return(0); } return(1); } static void ip6fw_report(struct ip6_fw *f, struct ip6_hdr *ip6, struct ifnet *rif, struct ifnet *oif, int off, int nxt) { static int counter; struct tcphdr *const tcp6 = (struct tcphdr *) ((caddr_t) ip6+ off); struct udphdr *const udp = (struct udphdr *) ((caddr_t) ip6+ off); struct icmp6_hdr *const icmp6 = (struct icmp6_hdr *) ((caddr_t) ip6+ off); int count; count = f ? f->fw_pcnt : ++counter; if (fw6_verbose_limit != 0 && count > fw6_verbose_limit) return; /* Print command name */ printf("ip6fw: %d ", f ? f->fw_number : -1); if (!f) printf("Refuse"); else switch (f->fw_flg & IPV6_FW_F_COMMAND) { case IPV6_FW_F_DENY: printf("Deny"); break; case IPV6_FW_F_REJECT: if (f->fw_reject_code == IPV6_FW_REJECT_RST) printf("Reset"); else printf("Unreach"); break; case IPV6_FW_F_ACCEPT: printf("Accept"); break; case IPV6_FW_F_COUNT: printf("Count"); break; case IPV6_FW_F_DIVERT: printf("Divert %d", f->fw_divert_port); break; case IPV6_FW_F_TEE: printf("Tee %d", f->fw_divert_port); break; case IPV6_FW_F_SKIPTO: printf("SkipTo %d", f->fw_skipto_rule); break; - default: + default: printf("UNKNOWN"); break; } printf(" "); switch (nxt) { case IPPROTO_TCP: printf("TCP "); print_ip6(&ip6->ip6_src); if (off > 0) printf(":%d ", ntohs(tcp6->th_sport)); else printf(" "); print_ip6(&ip6->ip6_dst); if (off > 0) printf(":%d", ntohs(tcp6->th_dport)); break; case IPPROTO_UDP: printf("UDP "); print_ip6(&ip6->ip6_src); if (off > 0) printf(":%d ", ntohs(udp->uh_sport)); else printf(" "); print_ip6(&ip6->ip6_dst); if (off > 0) printf(":%d", ntohs(udp->uh_dport)); break; case IPPROTO_ICMPV6: if (off > 0) printf("IPV6-ICMP:%u.%u ", icmp6->icmp6_type, icmp6->icmp6_code); else printf("IPV6-ICMP "); print_ip6(&ip6->ip6_src); printf(" "); print_ip6(&ip6->ip6_dst); break; default: printf("P:%d ", nxt); print_ip6(&ip6->ip6_src); printf(" "); print_ip6(&ip6->ip6_dst); break; } if (oif) printf(" out via %s", if_name(oif)); else if (rif) printf(" in via %s", if_name(rif)); printf("\n"); if (fw6_verbose_limit != 0 && count == fw6_verbose_limit) printf("ip6fw: limit reached on rule #%d\n", f ? f->fw_number : -1); } /* * Parameters: * * ip Pointer to packet header (struct ip6_hdr *) * hlen Packet header length * oif Outgoing interface, or NULL if packet is incoming * #ifndef IP6FW_DIVERT_RESTART * *cookie Ignore all divert/tee rules to this port (if non-zero) * #else * *cookie Skip up to the first rule past this rule number; * #endif * *m The packet; we set to NULL when/if we nuke it. * * Return value: * * 0 The packet is to be accepted and routed normally OR * the packet was denied/rejected and has been dropped; * in the latter case, *m is equal to NULL upon return. * port Divert the packet to port. */ static int ip6_fw_chk(struct ip6_hdr **pip6, struct ifnet *oif, u_int16_t *cookie, struct mbuf **m) { struct ip6_fw_chain *chain; struct ip6_fw *rule = NULL; struct ip6_hdr *ip6 = *pip6; struct ifnet *const rif = (*m)->m_pkthdr.rcvif; u_short offset = 0; int off = sizeof(struct ip6_hdr), nxt = ip6->ip6_nxt; u_short src_port, dst_port; #ifdef IP6FW_DIVERT_RESTART u_int16_t skipto = *cookie; #else u_int16_t ignport = ntohs(*cookie); #endif *cookie = 0; /* * Go down the chain, looking for enlightment * #ifdef IP6FW_DIVERT_RESTART * If we've been asked to start at a given rule immediatly, do so. * #endif */ chain = LIST_FIRST(&ip6_fw_chain); #ifdef IP6FW_DIVERT_RESTART if (skipto) { if (skipto >= 65535) goto dropit; while (chain && (chain->rule->fw_number <= skipto)) { chain = LIST_NEXT(chain, chain); } if (! chain) goto dropit; } #endif /* IP6FW_DIVERT_RESTART */ for (; chain; chain = LIST_NEXT(chain, chain)) { register struct ip6_fw *const f = chain->rule; if (oif) { /* Check direction outbound */ if (!(f->fw_flg & IPV6_FW_F_OUT)) continue; } else { /* Check direction inbound */ if (!(f->fw_flg & IPV6_FW_F_IN)) continue; } -#define IN6_ARE_ADDR_MASKEQUAL(x,y,z) (\ +#define IN6_ARE_ADDR_MASKEQUAL(x,y,z) (\ (((x)->s6_addr32[0] & (y)->s6_addr32[0]) == (z)->s6_addr32[0]) && \ (((x)->s6_addr32[1] & (y)->s6_addr32[1]) == (z)->s6_addr32[1]) && \ (((x)->s6_addr32[2] & (y)->s6_addr32[2]) == (z)->s6_addr32[2]) && \ (((x)->s6_addr32[3] & (y)->s6_addr32[3]) == (z)->s6_addr32[3])) /* If src-addr doesn't match, not this rule. */ if (((f->fw_flg & IPV6_FW_F_INVSRC) != 0) ^ (!IN6_ARE_ADDR_MASKEQUAL(&ip6->ip6_src,&f->fw_smsk,&f->fw_src))) continue; /* If dest-addr doesn't match, not this rule. */ if (((f->fw_flg & IPV6_FW_F_INVDST) != 0) ^ (!IN6_ARE_ADDR_MASKEQUAL(&ip6->ip6_dst,&f->fw_dmsk,&f->fw_dst))) continue; #undef IN6_ARE_ADDR_MASKEQUAL /* Interface check */ if ((f->fw_flg & IF6_FW_F_VIAHACK) == IF6_FW_F_VIAHACK) { struct ifnet *const iface = oif ? oif : rif; /* Backwards compatibility hack for "via" */ if (!iface || !iface_match(iface, &f->fw_in_if, f->fw_flg & IPV6_FW_F_OIFNAME)) continue; } else { /* Check receive interface */ if ((f->fw_flg & IPV6_FW_F_IIFACE) && (!rif || !iface_match(rif, &f->fw_in_if, f->fw_flg & IPV6_FW_F_IIFNAME))) continue; /* Check outgoing interface */ if ((f->fw_flg & IPV6_FW_F_OIFACE) && (!oif || !iface_match(oif, &f->fw_out_if, f->fw_flg & IPV6_FW_F_OIFNAME))) continue; } /* Check IP options */ if (!ip6opts_match(&ip6, f, m, &off, &nxt, &offset)) continue; /* Fragments */ if ((f->fw_flg & IPV6_FW_F_FRAG) && !offset) continue; /* Check protocol; if wildcard, match */ if (f->fw_prot == IPPROTO_IPV6) goto got_match; /* If different, don't match */ if (nxt != f->fw_prot) continue; -#define PULLUP_TO(len) do { \ +#define PULLUP_TO(len) do { \ if ((*m)->m_len < (len) \ && (*m = m_pullup(*m, (len))) == 0) { \ goto dropit; \ } \ *pip6 = ip6 = mtod(*m, struct ip6_hdr *); \ } while (0) /* Protocol specific checks */ switch (nxt) { case IPPROTO_TCP: { struct tcphdr *tcp6; if (offset == 1) { /* cf. RFC 1858 */ PULLUP_TO(off + 4); /* XXX ? */ goto bogusfrag; } if (offset != 0) { /* * TCP flags and ports aren't available in this * packet -- if this rule specified either one, * we consider the rule a non-match. */ if (f->fw_nports != 0 || f->fw_tcpf != f->fw_tcpnf) continue; break; } PULLUP_TO(off + 14); tcp6 = (struct tcphdr *) ((caddr_t)ip6 + off); if (f->fw_tcpf != f->fw_tcpnf && !tcp6flg_match(tcp6, f)) continue; src_port = ntohs(tcp6->th_sport); dst_port = ntohs(tcp6->th_dport); goto check_ports; } case IPPROTO_UDP: { struct udphdr *udp; if (offset != 0) { /* * Port specification is unavailable -- if this * rule specifies a port, we consider the rule * a non-match. */ if (f->fw_nports != 0) continue; break; } PULLUP_TO(off + 4); udp = (struct udphdr *) ((caddr_t)ip6 + off); src_port = ntohs(udp->uh_sport); dst_port = ntohs(udp->uh_dport); check_ports: if (!port_match6(&f->fw_pts[0], IPV6_FW_GETNSRCP(f), src_port, f->fw_flg & IPV6_FW_F_SRNG)) continue; if (!port_match6(&f->fw_pts[IPV6_FW_GETNSRCP(f)], IPV6_FW_GETNDSTP(f), dst_port, f->fw_flg & IPV6_FW_F_DRNG)) continue; break; } case IPPROTO_ICMPV6: { struct icmp6_hdr *icmp; if (offset != 0) /* Type isn't valid */ break; PULLUP_TO(off + 2); icmp = (struct icmp6_hdr *) ((caddr_t)ip6 + off); if (!icmp6type_match(icmp, f)) continue; break; } #undef PULLUP_TO bogusfrag: if (fw6_verbose) ip6fw_report(NULL, ip6, rif, oif, off, nxt); goto dropit; } got_match: #ifndef IP6FW_DIVERT_RESTART /* Ignore divert/tee rule if socket port is "ignport" */ switch (f->fw_flg & IPV6_FW_F_COMMAND) { case IPV6_FW_F_DIVERT: case IPV6_FW_F_TEE: if (f->fw_divert_port == ignport) continue; /* ignore this rule */ break; } #endif /* IP6FW_DIVERT_RESTART */ /* Update statistics */ f->fw_pcnt += 1; f->fw_bcnt += ntohs(ip6->ip6_plen); f->timestamp = time_second; /* Log to console if desired */ if ((f->fw_flg & IPV6_FW_F_PRN) && fw6_verbose) ip6fw_report(f, ip6, rif, oif, off, nxt); /* Take appropriate action */ switch (f->fw_flg & IPV6_FW_F_COMMAND) { case IPV6_FW_F_ACCEPT: return(0); case IPV6_FW_F_COUNT: continue; case IPV6_FW_F_DIVERT: #ifdef IP6FW_DIVERT_RESTART *cookie = f->fw_number; #else *cookie = htons(f->fw_divert_port); #endif /* IP6FW_DIVERT_RESTART */ return(f->fw_divert_port); case IPV6_FW_F_TEE: /* * XXX someday tee packet here, but beware that you * can't use m_copym() or m_copypacket() because * the divert input routine modifies the mbuf * (and these routines only increment reference * counts in the case of mbuf clusters), so need * to write custom routine. */ continue; case IPV6_FW_F_SKIPTO: #ifdef DIAGNOSTIC while (chain->chain.le_next && chain->chain.le_next->rule->fw_number < f->fw_skipto_rule) #else while (chain->chain.le_next->rule->fw_number < f->fw_skipto_rule) #endif chain = chain->chain.le_next; continue; } /* Deny/reject this packet using this rule */ rule = f; break; } #ifdef DIAGNOSTIC /* Rule 65535 should always be there and should always match */ if (!chain) panic("ip6_fw: chain"); #endif /* * At this point, we're going to drop the packet. * Send a reject notice if all of the following are true: * * - The packet matched a reject rule * - The packet is not an ICMP packet, or is an ICMP query packet * - The packet is not a multicast or broadcast packet */ if ((rule->fw_flg & IPV6_FW_F_COMMAND) == IPV6_FW_F_REJECT && (nxt != IPPROTO_ICMPV6 || is_icmp6_query(ip6, off)) && !((*m)->m_flags & (M_BCAST|M_MCAST)) && !IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { switch (rule->fw_reject_code) { case IPV6_FW_REJECT_RST: { struct tcphdr *const tcp = (struct tcphdr *) ((caddr_t)ip6 + off); struct { struct ip6_hdr ip6; struct tcphdr th; } ti; tcp_seq ack, seq; int flags; if (offset != 0 || (tcp->th_flags & TH_RST)) break; ti.ip6 = *ip6; ti.th = *tcp; NTOHL(ti.th.th_seq); NTOHL(ti.th.th_ack); ti.ip6.ip6_nxt = IPPROTO_TCP; if (ti.th.th_flags & TH_ACK) { ack = 0; seq = ti.th.th_ack; flags = TH_RST; } else { ack = ti.th.th_seq; if (((*m)->m_flags & M_PKTHDR) != 0) { ack += (*m)->m_pkthdr.len - off - (ti.th.th_off << 2); } else if (ip6->ip6_plen) { ack += ntohs(ip6->ip6_plen) + sizeof(*ip6) - off - (ti.th.th_off << 2); } else { m_freem(*m); *m = 0; break; } seq = 0; flags = TH_RST|TH_ACK; } bcopy(&ti, ip6, sizeof(ti)); m_freem(*m); *m = NULL; break; } default: /* Send an ICMP unreachable using code */ if (oif) (*m)->m_pkthdr.rcvif = oif; icmp6_error(*m, ICMP6_DST_UNREACH, rule->fw_reject_code, 0); *m = NULL; break; } } dropit: /* * Finally, drop the packet. */ if (*m) { m_freem(*m); *m = NULL; } return(0); } static int add_entry6(struct ip6_fw_head *chainptr, struct ip6_fw *frwl) { struct ip6_fw *ftmp = 0; struct ip6_fw_chain *fwc = 0, *fcp, *fcpl = 0; u_short nbr = 0; int s; fwc = malloc(sizeof *fwc, M_IP6FW, M_DONTWAIT); ftmp = malloc(sizeof *ftmp, M_IP6FW, M_DONTWAIT); if (!fwc || !ftmp) { dprintf(("%s malloc said no\n", err_prefix)); if (fwc) free(fwc, M_IP6FW); if (ftmp) free(ftmp, M_IP6FW); return (ENOSPC); } bcopy(frwl, ftmp, sizeof(struct ip6_fw)); ftmp->fw_in_if.fu_via_if.name[FW_IFNLEN - 1] = '\0'; ftmp->fw_pcnt = 0L; ftmp->fw_bcnt = 0L; fwc->rule = ftmp; - + s = splnet(); if (!chainptr->lh_first) { LIST_INSERT_HEAD(chainptr, fwc, chain); splx(s); return(0); } else if (ftmp->fw_number == (u_short)-1) { if (fwc) free(fwc, M_IP6FW); if (ftmp) free(ftmp, M_IP6FW); splx(s); dprintf(("%s bad rule number\n", err_prefix)); return (EINVAL); } /* If entry number is 0, find highest numbered rule and add 100 */ if (ftmp->fw_number == 0) { for (fcp = chainptr->lh_first; fcp; fcp = fcp->chain.le_next) { if (fcp->rule->fw_number != (u_short)-1) nbr = fcp->rule->fw_number; else break; } if (nbr < (u_short)-1 - 100) nbr += 100; ftmp->fw_number = nbr; } /* Got a valid number; now insert it, keeping the list ordered */ for (fcp = chainptr->lh_first; fcp; fcp = fcp->chain.le_next) { if (fcp->rule->fw_number > ftmp->fw_number) { if (fcpl) { LIST_INSERT_AFTER(fcpl, fwc, chain); } else { LIST_INSERT_HEAD(chainptr, fwc, chain); } break; } else { fcpl = fcp; } } splx(s); return (0); } static int del_entry6(struct ip6_fw_head *chainptr, u_short number) { struct ip6_fw_chain *fcp; int s; s = splnet(); fcp = chainptr->lh_first; if (number != (u_short)-1) { for (; fcp; fcp = fcp->chain.le_next) { if (fcp->rule->fw_number == number) { LIST_REMOVE(fcp, chain); splx(s); free(fcp->rule, M_IP6FW); free(fcp, M_IP6FW); return 0; } } } splx(s); return (EINVAL); } static int zero_entry6(struct mbuf *m) { struct ip6_fw *frwl; struct ip6_fw_chain *fcp; int s; if (m) { if (m->m_len != sizeof(struct ip6_fw)) return(EINVAL); frwl = mtod(m, struct ip6_fw *); } else frwl = NULL; /* * It's possible to insert multiple chain entries with the * same number, so we don't stop after finding the first * match if zeroing a specific entry. */ s = splnet(); for (fcp = ip6_fw_chain.lh_first; fcp; fcp = fcp->chain.le_next) if (!frwl || frwl->fw_number == fcp->rule->fw_number) { fcp->rule->fw_bcnt = fcp->rule->fw_pcnt = 0; fcp->rule->timestamp = 0; } splx(s); if (fw6_verbose) { if (frwl) printf("ip6fw: Entry %d cleared.\n", frwl->fw_number); else printf("ip6fw: Accounting cleared.\n"); } return(0); } static struct ip6_fw * check_ip6fw_mbuf(struct mbuf *m) { /* Check length */ if (m->m_len != sizeof(struct ip6_fw)) { dprintf(("%s len=%d, want %d\n", err_prefix, m->m_len, sizeof(struct ip6_fw))); return (NULL); } return(check_ip6fw_struct(mtod(m, struct ip6_fw *))); } static struct ip6_fw * check_ip6fw_struct(struct ip6_fw *frwl) { /* Check for invalid flag bits */ if ((frwl->fw_flg & ~IPV6_FW_F_MASK) != 0) { dprintf(("%s undefined flag bits set (flags=%x)\n", err_prefix, frwl->fw_flg)); return (NULL); } /* Must apply to incoming or outgoing (or both) */ if (!(frwl->fw_flg & (IPV6_FW_F_IN | IPV6_FW_F_OUT))) { dprintf(("%s neither in nor out\n", err_prefix)); return (NULL); } /* Empty interface name is no good */ if (((frwl->fw_flg & IPV6_FW_F_IIFNAME) && !*frwl->fw_in_if.fu_via_if.name) || ((frwl->fw_flg & IPV6_FW_F_OIFNAME) && !*frwl->fw_out_if.fu_via_if.name)) { dprintf(("%s empty interface name\n", err_prefix)); return (NULL); } /* Sanity check interface matching */ if ((frwl->fw_flg & IF6_FW_F_VIAHACK) == IF6_FW_F_VIAHACK) { ; /* allow "via" backwards compatibility */ } else if ((frwl->fw_flg & IPV6_FW_F_IN) && (frwl->fw_flg & IPV6_FW_F_OIFACE)) { dprintf(("%s outgoing interface check on incoming\n", err_prefix)); return (NULL); } /* Sanity check port ranges */ if ((frwl->fw_flg & IPV6_FW_F_SRNG) && IPV6_FW_GETNSRCP(frwl) < 2) { dprintf(("%s src range set but n_src_p=%d\n", err_prefix, IPV6_FW_GETNSRCP(frwl))); return (NULL); } if ((frwl->fw_flg & IPV6_FW_F_DRNG) && IPV6_FW_GETNDSTP(frwl) < 2) { dprintf(("%s dst range set but n_dst_p=%d\n", err_prefix, IPV6_FW_GETNDSTP(frwl))); return (NULL); } if (IPV6_FW_GETNSRCP(frwl) + IPV6_FW_GETNDSTP(frwl) > IPV6_FW_MAX_PORTS) { dprintf(("%s too many ports (%d+%d)\n", err_prefix, IPV6_FW_GETNSRCP(frwl), IPV6_FW_GETNDSTP(frwl))); return (NULL); } /* * Protocols other than TCP/UDP don't use port range */ if ((frwl->fw_prot != IPPROTO_TCP) && (frwl->fw_prot != IPPROTO_UDP) && (IPV6_FW_GETNSRCP(frwl) || IPV6_FW_GETNDSTP(frwl))) { dprintf(("%s port(s) specified for non TCP/UDP rule\n", err_prefix)); return(NULL); } /* * Rather than modify the entry to make such entries work, * we reject this rule and require user level utilities * to enforce whatever policy they deem appropriate. */ if ((frwl->fw_src.s6_addr32[0] & (~frwl->fw_smsk.s6_addr32[0])) || (frwl->fw_src.s6_addr32[1] & (~frwl->fw_smsk.s6_addr32[1])) || (frwl->fw_src.s6_addr32[2] & (~frwl->fw_smsk.s6_addr32[2])) || (frwl->fw_src.s6_addr32[3] & (~frwl->fw_smsk.s6_addr32[3])) || (frwl->fw_dst.s6_addr32[0] & (~frwl->fw_dmsk.s6_addr32[0])) || (frwl->fw_dst.s6_addr32[1] & (~frwl->fw_dmsk.s6_addr32[1])) || (frwl->fw_dst.s6_addr32[2] & (~frwl->fw_dmsk.s6_addr32[2])) || (frwl->fw_dst.s6_addr32[3] & (~frwl->fw_dmsk.s6_addr32[3]))) { dprintf(("%s rule never matches\n", err_prefix)); return(NULL); } if ((frwl->fw_flg & IPV6_FW_F_FRAG) && (frwl->fw_prot == IPPROTO_UDP || frwl->fw_prot == IPPROTO_TCP)) { if (frwl->fw_nports) { dprintf(("%s cannot mix 'frag' and ports\n", err_prefix)); return(NULL); } if (frwl->fw_prot == IPPROTO_TCP && frwl->fw_tcpf != frwl->fw_tcpnf) { dprintf(("%s cannot mix 'frag' with TCP flags\n", err_prefix)); return(NULL); } } /* Check command specific stuff */ switch (frwl->fw_flg & IPV6_FW_F_COMMAND) { case IPV6_FW_F_REJECT: if (frwl->fw_reject_code >= 0x100 && !(frwl->fw_prot == IPPROTO_TCP && frwl->fw_reject_code == IPV6_FW_REJECT_RST)) { dprintf(("%s unknown reject code\n", err_prefix)); return(NULL); } break; case IPV6_FW_F_DIVERT: /* Diverting to port zero is invalid */ case IPV6_FW_F_TEE: if (frwl->fw_divert_port == 0) { dprintf(("%s can't divert to port 0\n", err_prefix)); return (NULL); } break; case IPV6_FW_F_DENY: case IPV6_FW_F_ACCEPT: case IPV6_FW_F_COUNT: case IPV6_FW_F_SKIPTO: break; default: dprintf(("%s invalid command\n", err_prefix)); return(NULL); } return frwl; } static int ip6_fw_ctl(int stage, struct mbuf **mm) { int error; struct mbuf *m; if (stage == IPV6_FW_GET) { struct ip6_fw_chain *fcp = ip6_fw_chain.lh_first; *mm = m = m_get(M_WAIT, MT_DATA); /* XXX */ if (!m) return(ENOBUFS); if (sizeof *(fcp->rule) > MLEN) { MCLGET(m, M_WAIT); if ((m->m_flags & M_EXT) == 0) { m_free(m); return(ENOBUFS); } } for (; fcp; fcp = fcp->chain.le_next) { memcpy(m->m_data, fcp->rule, sizeof *(fcp->rule)); m->m_len = sizeof *(fcp->rule); m->m_next = m_get(M_WAIT, MT_DATA); /* XXX */ if (!m->m_next) { m_freem(*mm); return(ENOBUFS); } m = m->m_next; if (sizeof *(fcp->rule) > MLEN) { MCLGET(m, M_WAIT); if ((m->m_flags & M_EXT) == 0) { m_freem(*mm); return(ENOBUFS); } } m->m_len = 0; } return (0); } m = *mm; /* only allow get calls if secure mode > 2 */ if (securelevel > 2) { if (m) { (void)m_freem(m); *mm = 0; } return(EPERM); } if (stage == IPV6_FW_FLUSH) { while (ip6_fw_chain.lh_first != NULL && ip6_fw_chain.lh_first->rule->fw_number != (u_short)-1) { struct ip6_fw_chain *fcp = ip6_fw_chain.lh_first; int s = splnet(); LIST_REMOVE(ip6_fw_chain.lh_first, chain); splx(s); free(fcp->rule, M_IP6FW); free(fcp, M_IP6FW); } if (m) { (void)m_freem(m); *mm = 0; } return (0); } if (stage == IPV6_FW_ZERO) { error = zero_entry6(m); if (m) { (void)m_freem(m); *mm = 0; } return (error); } if (m == NULL) { printf("%s NULL mbuf ptr\n", err_prefix); return (EINVAL); } if (stage == IPV6_FW_ADD) { struct ip6_fw *frwl = check_ip6fw_mbuf(m); if (!frwl) error = EINVAL; else error = add_entry6(&ip6_fw_chain, frwl); if (m) { (void)m_freem(m); *mm = 0; } return error; } if (stage == IPV6_FW_DEL) { if (m->m_len != sizeof(struct ip6_fw)) { dprintf(("%s len=%d, want %d\n", err_prefix, m->m_len, sizeof(struct ip6_fw))); error = EINVAL; } else if (mtod(m, struct ip6_fw *)->fw_number == (u_short)-1) { dprintf(("%s can't delete rule 65535\n", err_prefix)); error = EINVAL; } else error = del_entry6(&ip6_fw_chain, mtod(m, struct ip6_fw *)->fw_number); if (m) { (void)m_freem(m); *mm = 0; } return error; } dprintf(("%s unknown request %d\n", err_prefix, stage)); if (m) { (void)m_freem(m); *mm = 0; } return (EINVAL); } void ip6_fw_init(void) { struct ip6_fw default_rule; ip6_fw_chk_ptr = ip6_fw_chk; ip6_fw_ctl_ptr = ip6_fw_ctl; LIST_INIT(&ip6_fw_chain); bzero(&default_rule, sizeof default_rule); default_rule.fw_prot = IPPROTO_IPV6; default_rule.fw_number = (u_short)-1; #ifdef IPV6FIREWALL_DEFAULT_TO_ACCEPT default_rule.fw_flg |= IPV6_FW_F_ACCEPT; #else default_rule.fw_flg |= IPV6_FW_F_DENY; #endif default_rule.fw_flg |= IPV6_FW_F_IN | IPV6_FW_F_OUT; if (check_ip6fw_struct(&default_rule) == NULL || add_entry6(&ip6_fw_chain, &default_rule)) panic(__FUNCTION__); printf("IPv6 packet filtering initialized, "); #ifdef IPV6FIREWALL_DEFAULT_TO_ACCEPT printf("default to accept, "); #endif #ifndef IPV6FIREWALL_VERBOSE printf("logging disabled\n"); #else if (fw6_verbose_limit == 0) printf("unlimited logging\n"); else printf("logging limited to %d packets/entry\n", fw6_verbose_limit); #endif } Index: head/sys/netinet6/ip6_fw.h =================================================================== --- head/sys/netinet6/ip6_fw.h (revision 62586) +++ head/sys/netinet6/ip6_fw.h (revision 62587) @@ -1,202 +1,196 @@ +/* $FreeBSD$ */ +/* $KAME: ip6_fw.h,v 1.3 2000/04/06 08:30:44 sumikawa Exp $ */ + /* * Copyright (c) 1993 Daniel Boulet * Copyright (c) 1994 Ugen J.S.Antsilevich * * Redistribution and use in source forms, with and without modification, * are permitted provided that this entire comment appears intact. * * Redistribution in binary form may occur without any restrictions. * Obviously, it would be nice if you gave credit where credit is due * but requiring it would be too onerous. * * This software is provided ``AS IS'' without any warranties of any kind. * - * $Id: ip6_fw.h,v 1.1 1999/08/06 14:10:09 itojun Exp $ - * $FreeBSD$ */ #ifndef _IP6_FW_H #define _IP6_FW_H #include /* * This union structure identifies an interface, either explicitly * by name or implicitly by IP address. The flags IP_FW_F_IIFNAME * and IP_FW_F_OIFNAME say how to interpret this structure. An * interface unit number of -1 matches any unit number, while an * IP address of 0.0.0.0 indicates matches any interface. * * The receive and transmit interfaces are only compared against the * the packet if the corresponding bit (IP_FW_F_IIFACE or IP_FW_F_OIFACE) * is set. Note some packets lack a receive or transmit interface * (in which case the missing "interface" never matches). */ union ip6_fw_if { - struct in6_addr fu_via_ip6; /* Specified by IPv6 address */ - struct { /* Specified by interface name */ + struct in6_addr fu_via_ip6; /* Specified by IPv6 address */ + struct { /* Specified by interface name */ #define FW_IFNLEN IFNAMSIZ - char name[FW_IFNLEN]; - short unit; /* -1 means match any unit */ + char name[FW_IFNLEN]; + short unit; /* -1 means match any unit */ } fu_via_if; }; /* * Format of an IP firewall descriptor * * fw_src, fw_dst, fw_smsk, fw_dmsk are always stored in network byte order. * fw_flg and fw_n*p are stored in host byte order (of course). * Port numbers are stored in HOST byte order. * Warning: setsockopt() will fail if sizeof(struct ip_fw) > MLEN (108) */ struct ip6_fw { - u_long fw_pcnt,fw_bcnt; /* Packet and byte counters */ - struct in6_addr fw_src, fw_dst; /* Source and destination IPv6 addr */ - /* Mask for src and dest IPv6 addr */ - struct in6_addr fw_smsk, fw_dmsk; - u_short fw_number; /* Rule number */ - u_short fw_flg; /* Flags word */ -#define IPV6_FW_MAX_PORTS 10 /* A reasonable maximum */ - /* Array of port numbers to match */ - u_short fw_pts[IPV6_FW_MAX_PORTS]; - u_char fw_ip6opt,fw_ip6nopt; /* IPv6 options set/unset */ - u_char fw_tcpf,fw_tcpnf; /* TCP flags set/unset */ -#define IPV6_FW_ICMPTYPES_DIM (32 / (sizeof(unsigned) * 8)) - /* ICMP types bitmap */ - unsigned fw_icmp6types[IPV6_FW_ICMPTYPES_DIM]; - long timestamp; /* timestamp (tv_sec) of last match */ - /* Incoming and outgoing interfaces */ - union ip6_fw_if fw_in_if, fw_out_if; - union { - u_short fu_divert_port; /* Divert/tee port (options IP6DIVERT) */ - u_short fu_skipto_rule; /* SKIPTO command rule number */ - u_short fu_reject_code; /* REJECT response code */ - } fw_un; - u_char fw_prot; /* IPv6 protocol */ - u_char fw_nports; /* N'of src ports and # of dst ports */ - /* in ports array (dst ports follow */ - /* src ports; max of 10 ports in all; */ - /* count of 0 means match all ports) */ + u_long fw_pcnt,fw_bcnt; /* Packet and byte counters */ + struct in6_addr fw_src, fw_dst; /* Source and destination IPv6 addr */ + struct in6_addr fw_smsk, fw_dmsk; /* Mask for src and dest IPv6 addr */ + u_short fw_number; /* Rule number */ + u_short fw_flg; /* Flags word */ +#define IPV6_FW_MAX_PORTS 10 /* A reasonable maximum */ + u_short fw_pts[IPV6_FW_MAX_PORTS]; /* Array of port numbers to match */ + u_char fw_ip6opt,fw_ip6nopt; /* IPv6 options set/unset */ + u_char fw_tcpf,fw_tcpnf; /* TCP flags set/unset */ +#define IPV6_FW_ICMPTYPES_DIM (32 / (sizeof(unsigned) * 8)) + unsigned fw_icmp6types[IPV6_FW_ICMPTYPES_DIM]; /* ICMP types bitmap */ + long timestamp; /* timestamp (tv_sec) of last match */ + union ip6_fw_if fw_in_if, fw_out_if;/* Incoming and outgoing interfaces */ + union { + u_short fu_divert_port; /* Divert/tee port (options IP6DIVERT) */ + u_short fu_skipto_rule; /* SKIPTO command rule number */ + u_short fu_reject_code; /* REJECT response code */ + } fw_un; + u_char fw_prot; /* IPv6 protocol */ + u_char fw_nports; /* N'of src ports and # of dst ports */ + /* in ports array (dst ports follow */ + /* src ports; max of 10 ports in all; */ + /* count of 0 means match all ports) */ }; -#define IPV6_FW_GETNSRCP(rule) ((rule)->fw_nports & 0x0f) -#define IPV6_FW_SETNSRCP(rule, n) do { \ +#define IPV6_FW_GETNSRCP(rule) ((rule)->fw_nports & 0x0f) +#define IPV6_FW_SETNSRCP(rule, n) do { \ (rule)->fw_nports &= ~0x0f; \ (rule)->fw_nports |= (n); \ } while (0) -#define IPV6_FW_GETNDSTP(rule) ((rule)->fw_nports >> 4) -#define IPV6_FW_SETNDSTP(rule, n) do { \ +#define IPV6_FW_GETNDSTP(rule) ((rule)->fw_nports >> 4) +#define IPV6_FW_SETNDSTP(rule, n) do { \ (rule)->fw_nports &= ~0xf0; \ (rule)->fw_nports |= (n) << 4;\ } while (0) -#define fw_divert_port fw_un.fu_divert_port -#define fw_skipto_rule fw_un.fu_skipto_rule -#define fw_reject_code fw_un.fu_reject_code +#define fw_divert_port fw_un.fu_divert_port +#define fw_skipto_rule fw_un.fu_skipto_rule +#define fw_reject_code fw_un.fu_reject_code -struct ip6_fw_chain { - LIST_ENTRY(ip6_fw_chain) chain; - struct ip6_fw *rule; +struct ip6_fw_chain { + LIST_ENTRY(ip6_fw_chain) chain; + struct ip6_fw *rule; }; /* * Values for "flags" field . */ -#define IPV6_FW_F_IN 0x0001 /* Check inbound packets */ -#define IPV6_FW_F_OUT 0x0002 /* Check outbound packets */ -#define IPV6_FW_F_IIFACE 0x0004 /* Apply inbound interface test */ -#define IPV6_FW_F_OIFACE 0x0008 /* Apply outbound interface test */ +#define IPV6_FW_F_IN 0x0001 /* Check inbound packets */ +#define IPV6_FW_F_OUT 0x0002 /* Check outbound packets */ +#define IPV6_FW_F_IIFACE 0x0004 /* Apply inbound interface test */ +#define IPV6_FW_F_OIFACE 0x0008 /* Apply outbound interface test */ -#define IPV6_FW_F_COMMAND 0x0070 /* Mask for type of chain entry: */ -#define IPV6_FW_F_DENY 0x0000 /* This is a deny rule */ -#define IPV6_FW_F_REJECT 0x0010 /* Deny and send a response packet */ -#define IPV6_FW_F_ACCEPT 0x0020 /* This is an accept rule */ -#define IPV6_FW_F_COUNT 0x0030 /* This is a count rule */ -#define IPV6_FW_F_DIVERT 0x0040 /* This is a divert rule */ -#define IPV6_FW_F_TEE 0x0050 /* This is a tee rule */ -#define IPV6_FW_F_SKIPTO 0x0060 /* This is a skipto rule */ +#define IPV6_FW_F_COMMAND 0x0070 /* Mask for type of chain entry: */ +#define IPV6_FW_F_DENY 0x0000 /* This is a deny rule */ +#define IPV6_FW_F_REJECT 0x0010 /* Deny and send a response packet */ +#define IPV6_FW_F_ACCEPT 0x0020 /* This is an accept rule */ +#define IPV6_FW_F_COUNT 0x0030 /* This is a count rule */ +#define IPV6_FW_F_DIVERT 0x0040 /* This is a divert rule */ +#define IPV6_FW_F_TEE 0x0050 /* This is a tee rule */ +#define IPV6_FW_F_SKIPTO 0x0060 /* This is a skipto rule */ -#define IPV6_FW_F_PRN 0x0080 /* Print if this rule matches */ +#define IPV6_FW_F_PRN 0x0080 /* Print if this rule matches */ -#define IPV6_FW_F_SRNG 0x0100 /* The first two src ports are a min * - * and max range (stored in host byte * - * order). */ +#define IPV6_FW_F_SRNG 0x0100 /* The first two src ports are a min * + * and max range (stored in host byte * + * order). */ -#define IPV6_FW_F_DRNG 0x0200 /* The first two dst ports are a min * +#define IPV6_FW_F_DRNG 0x0200 /* The first two dst ports are a min * * and max range (stored in host byte * * order). */ -/* In interface by name/unit (not IP) */ -#define IPV6_FW_F_IIFNAME 0x0400 -/* Out interface by name/unit (not IP) */ -#define IPV6_FW_F_OIFNAME 0x0800 +#define IPV6_FW_F_IIFNAME 0x0400 /* In interface by name/unit (not IP) */ +#define IPV6_FW_F_OIFNAME 0x0800 /* Out interface by name/unit (not IP) */ -#define IPV6_FW_F_INVSRC 0x1000 /* Invert sense of src check */ -#define IPV6_FW_F_INVDST 0x2000 /* Invert sense of dst check */ +#define IPV6_FW_F_INVSRC 0x1000 /* Invert sense of src check */ +#define IPV6_FW_F_INVDST 0x2000 /* Invert sense of dst check */ -#define IPV6_FW_F_FRAG 0x4000 /* Fragment */ +#define IPV6_FW_F_FRAG 0x4000 /* Fragment */ -#define IPV6_FW_F_ICMPBIT 0x8000 /* ICMP type bitmap is valid */ +#define IPV6_FW_F_ICMPBIT 0x8000 /* ICMP type bitmap is valid */ -#define IPV6_FW_F_MASK 0xFFFF /* All possible flag bits mask */ +#define IPV6_FW_F_MASK 0xFFFF /* All possible flag bits mask */ /* * For backwards compatibility with rules specifying "via iface" but * not restricted to only "in" or "out" packets, we define this combination * of bits to represent this configuration. */ -#define IF6_FW_F_VIAHACK (IPV6_FW_F_IN|IPV6_FW_F_OUT|IPV6_FW_F_IIFACE|\ - IPV6_FW_F_OIFACE) +#define IF6_FW_F_VIAHACK (IPV6_FW_F_IN|IPV6_FW_F_OUT|IPV6_FW_F_IIFACE|IPV6_FW_F_OIFACE) /* * Definitions for REJECT response codes. * Values less than 256 correspond to ICMP unreachable codes. */ -#define IPV6_FW_REJECT_RST 0x0100 /* TCP packets: send RST */ +#define IPV6_FW_REJECT_RST 0x0100 /* TCP packets: send RST */ /* * Definitions for IPv6 option names. */ -#define IPV6_FW_IP6OPT_HOPOPT 0x01 -#define IPV6_FW_IP6OPT_ROUTE 0x02 -#define IPV6_FW_IP6OPT_FRAG 0x04 -#define IPV6_FW_IP6OPT_ESP 0x08 -#define IPV6_FW_IP6OPT_AH 0x10 -#define IPV6_FW_IP6OPT_NONXT 0x20 -#define IPV6_FW_IP6OPT_OPTS 0x40 +#define IPV6_FW_IP6OPT_HOPOPT 0x01 +#define IPV6_FW_IP6OPT_ROUTE 0x02 +#define IPV6_FW_IP6OPT_FRAG 0x04 +#define IPV6_FW_IP6OPT_ESP 0x08 +#define IPV6_FW_IP6OPT_AH 0x10 +#define IPV6_FW_IP6OPT_NONXT 0x20 +#define IPV6_FW_IP6OPT_OPTS 0x40 /* * Definitions for TCP flags. */ -#define IPV6_FW_TCPF_FIN TH_FIN -#define IPV6_FW_TCPF_SYN TH_SYN -#define IPV6_FW_TCPF_RST TH_RST -#define IPV6_FW_TCPF_PSH TH_PUSH -#define IPV6_FW_TCPF_ACK TH_ACK -#define IPV6_FW_TCPF_URG TH_URG -#define IPV6_FW_TCPF_ESTAB 0x40 +#define IPV6_FW_TCPF_FIN TH_FIN +#define IPV6_FW_TCPF_SYN TH_SYN +#define IPV6_FW_TCPF_RST TH_RST +#define IPV6_FW_TCPF_PSH TH_PUSH +#define IPV6_FW_TCPF_ACK TH_ACK +#define IPV6_FW_TCPF_URG TH_URG +#define IPV6_FW_TCPF_ESTAB 0x40 /* * Main firewall chains definitions and global var's definitions. */ #ifdef _KERNEL /* * Function definitions. */ -void ip6_fw_init(void); +void ip6_fw_init(void); /* Firewall hooks */ -struct ip6_hdr; -typedef int ip6_fw_chk_t __P((struct ip6_hdr**, struct ifnet*, +struct ip6_hdr; +typedef int ip6_fw_chk_t __P((struct ip6_hdr**, struct ifnet*, u_short *, struct mbuf**)); -typedef int ip6_fw_ctl_t __P((int, struct mbuf**)); +typedef int ip6_fw_ctl_t __P((int, struct mbuf**)); extern ip6_fw_chk_t *ip6_fw_chk_ptr; extern ip6_fw_ctl_t *ip6_fw_ctl_ptr; #endif /* _KERNEL */ #endif /* _IP6_FW_H */ Index: head/sys/netinet6/ip6_input.c =================================================================== --- head/sys/netinet6/ip6_input.c (revision 62586) +++ head/sys/netinet6/ip6_input.c (revision 62587) @@ -1,1021 +1,1283 @@ +/* $FreeBSD$ */ +/* $KAME: ip6_input.c,v 1.95 2000/07/02 07:49:37 jinmei Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* * Copyright (c) 1982, 1986, 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)ip_input.c 8.2 (Berkeley) 1/4/94 */ -#include "opt_ipsec.h" #include "opt_ip6fw.h" +#include "opt_inet.h" +#include "opt_inet6.h" +#include "opt_ipsec.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#ifdef INET #include #include -#include +#endif /*INET*/ +#include #include -#include #include -#include +#include +#include #include #include #include #ifdef IPV6FIREWALL #include #endif -#ifdef ALTQ -#include -#endif - #include /* we need it for NLOOP. */ #include "loop.h" - #include "faith.h" #include "gif.h" #include -extern struct domain inet6domain; -extern struct ip6protosw inet6sw[]; +extern struct domain inet6domain; +extern struct ip6protosw inet6sw[]; -u_char ip6_protox[IPPROTO_MAX]; -static int ip6qmaxlen = IFQ_MAXLEN; -struct in6_ifaddr *in6_ifaddr; +u_char ip6_protox[IPPROTO_MAX]; +static int ip6qmaxlen = IFQ_MAXLEN; +struct in6_ifaddr *in6_ifaddr; -int ip6_forward_srcrt; /* XXX */ -int ip6_sourcecheck; /* XXX */ -int ip6_sourcecheck_interval; /* XXX */ +int ip6_forward_srcrt; /* XXX */ +int ip6_sourcecheck; /* XXX */ +int ip6_sourcecheck_interval; /* XXX */ +const int int6intrq_present = 1; -const int int6intrq_present = 1; - #ifdef IPV6FIREWALL /* firewall hooks */ -ip6_fw_chk_t *ip6_fw_chk_ptr; -ip6_fw_ctl_t *ip6_fw_ctl_ptr; +ip6_fw_chk_t *ip6_fw_chk_ptr; +ip6_fw_ctl_t *ip6_fw_ctl_ptr; #endif -struct ip6stat ip6stat; +struct ip6stat ip6stat; -static void ip6_init2 __P((void *)); +static void ip6_init2 __P((void *)); -static int ip6_hopopts_input __P((u_int32_t *, u_int32_t *, struct mbuf **, int *)); - -#if defined(PTR) -extern int ip6_protocol_tr; - -int ptr_in6 __P((struct mbuf *, struct mbuf **)); -extern void ip_forward __P((struct mbuf *, int)); +static int ip6_hopopts_input __P((u_int32_t *, u_int32_t *, struct mbuf **, int *)); +#ifdef PULLDOWN_TEST +static struct mbuf *ip6_pullexthdr __P((struct mbuf *, size_t, int)); #endif /* * IP6 initialization: fill in IP6 protocol switch table. * All protocols not implemented in kernel go to raw IP6 protocol handler. */ void ip6_init() { register struct ip6protosw *pr; register int i; struct timeval tv; pr = (struct ip6protosw *)pffindproto(PF_INET6, IPPROTO_RAW, SOCK_RAW); if (pr == 0) panic("ip6_init"); for (i = 0; i < IPPROTO_MAX; i++) ip6_protox[i] = pr - inet6sw; for (pr = (struct ip6protosw *)inet6domain.dom_protosw; pr < (struct ip6protosw *)inet6domain.dom_protoswNPROTOSW; pr++) if (pr->pr_domain->dom_family == PF_INET6 && pr->pr_protocol && pr->pr_protocol != IPPROTO_RAW) ip6_protox[pr->pr_protocol] = pr - inet6sw; ip6intrq.ifq_maxlen = ip6qmaxlen; register_netisr(NETISR_IPV6, ip6intr); nd6_init(); frag6_init(); #ifdef IPV6FIREWALL ip6_fw_init(); #endif /* * in many cases, random() here does NOT return random number * as initialization during bootstrap time occur in fixed order. */ microtime(&tv); ip6_flow_seq = random() ^ tv.tv_usec; } static void ip6_init2(dummy) void *dummy; { - int i; - int ret; - /* get EUI64 from somewhere */ - ret = in6_ifattach_getifid(NULL); - /* * to route local address of p2p link to loopback, * assign loopback address first. */ - for (i = 0; i < NLOOP; i++) - in6_ifattach(&loif[i], IN6_IFT_LOOP, NULL, 0); + in6_ifattach(&loif[0], NULL); - /* attach pseudo interfaces */ - if (ret == 0) - in6_ifattach_p2p(); - /* nd6_timer_init */ timeout(nd6_timer, (caddr_t)0, hz); /* router renumbering prefix list maintenance */ timeout(in6_rr_timer, (caddr_t)0, hz); } /* cheat */ /* This must be after route_init(), which is now SI_ORDER_THIRD */ SYSINIT(netinet6init2, SI_SUB_PROTO_DOMAIN, SI_ORDER_MIDDLE, ip6_init2, NULL); /* * IP6 input interrupt handling. Just pass the packet to ip6_input. */ void ip6intr() { int s; struct mbuf *m; for (;;) { s = splimp(); IF_DEQUEUE(&ip6intrq, m); splx(s); if (m == 0) return; ip6_input(m); } } extern struct route_in6 ip6_forward_rt; void ip6_input(m) struct mbuf *m; { struct ip6_hdr *ip6; int off = sizeof(struct ip6_hdr), nest; u_int32_t plen; u_int32_t rtalert = ~0; int nxt, ours = 0; struct ifnet *deliverifp = NULL; #ifdef IPSEC /* * should the inner packet be considered authentic? * see comment in ah4_input(). */ if (m) { m->m_flags &= ~M_AUTHIPHDR; m->m_flags &= ~M_AUTHIPDGM; } #endif /* * mbuf statistics by kazu */ if (m->m_flags & M_EXT) { if (m->m_next) ip6stat.ip6s_mext2m++; else ip6stat.ip6s_mext1++; } else { if (m->m_next) { if (m->m_flags & M_LOOP) { ip6stat.ip6s_m2m[loif[0].if_index]++; /*XXX*/ } else if (m->m_pkthdr.rcvif->if_index <= 31) ip6stat.ip6s_m2m[m->m_pkthdr.rcvif->if_index]++; else ip6stat.ip6s_m2m[0]++; } else ip6stat.ip6s_m1++; } in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_receive); ip6stat.ip6s_total++; +#ifndef PULLDOWN_TEST + /* XXX is the line really necessary? */ IP6_EXTHDR_CHECK(m, 0, sizeof(struct ip6_hdr), /*nothing*/); +#endif if (m->m_len < sizeof(struct ip6_hdr)) { struct ifnet *inifp; inifp = m->m_pkthdr.rcvif; if ((m = m_pullup(m, sizeof(struct ip6_hdr))) == 0) { ip6stat.ip6s_toosmall++; in6_ifstat_inc(inifp, ifs6_in_hdrerr); return; } } ip6 = mtod(m, struct ip6_hdr *); if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) { ip6stat.ip6s_badvers++; in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_hdrerr); goto bad; } ip6stat.ip6s_nxthist[ip6->ip6_nxt]++; #ifdef IPV6FIREWALL /* * Check with the firewall... */ if (ip6_fw_chk_ptr) { u_short port = 0; /* If ipfw says divert, we have to just drop packet */ /* use port as a dummy argument */ if ((*ip6_fw_chk_ptr)(&ip6, NULL, &port, &m)) { m_freem(m); m = NULL; } if (!m) return; } #endif -#ifdef ALTQ - if (altq_input != NULL && (*altq_input)(m, AF_INET6) == 0) { - /* packet is dropped by traffic conditioner */ - return; - } -#endif - /* * Scope check */ if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_src) || IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_dst)) { ip6stat.ip6s_badscope++; in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_addrerr); goto bad; } + + /* + * Don't check IPv4 mapped address here. SIIT assumes that + * routers would forward IPv6 native packets with IPv4 mapped + * address normally. + */ +#if 0 + /* + * Reject packets with IPv4 compatible addresses (auto tunnel). + * + * The code forbids auto tunnel relay case in RFC1933 (the check is + * stronger than RFC1933). We may want to re-enable it if mech-xx + * is revised to forbid relaying case. + */ + if (IN6_IS_ADDR_V4COMPAT(&ip6->ip6_src) || + IN6_IS_ADDR_V4COMPAT(&ip6->ip6_dst)) { + ip6stat.ip6s_badscope++; + in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_addrerr); + goto bad; + } +#endif if (IN6_IS_ADDR_LOOPBACK(&ip6->ip6_src) || IN6_IS_ADDR_LOOPBACK(&ip6->ip6_dst)) { - if ((m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) == 0) { + if (m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) { + ours = 1; + deliverifp = m->m_pkthdr.rcvif; + goto hbhcheck; + } else { ip6stat.ip6s_badscope++; in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_addrerr); goto bad; } } - if (m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) { - if (IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_dst)) { - ours = 1; - deliverifp = m->m_pkthdr.rcvif; - goto hbhcheck; - } - } else { +#ifndef FAKE_LOOPBACK_IF + if ((m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) == 0) +#else + if (1) +#endif + { if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) ip6->ip6_src.s6_addr16[1] = htons(m->m_pkthdr.rcvif->if_index); if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) ip6->ip6_dst.s6_addr16[1] = htons(m->m_pkthdr.rcvif->if_index); } -#if defined(PTR) /* - * + * XXX we need this since we do not have "goto ours" hack route + * for some of our ifaddrs on loopback interface. + * we should correct it by changing in6_ifattach to install + * "goto ours" hack route. */ - if (ip6_protocol_tr) - { - struct mbuf *m1 = NULL; - - switch (ptr_in6(m, &m1)) - { - case IPPROTO_IP: goto mcastcheck; - case IPPROTO_IPV4: ip_forward(m1, 0); break; - case IPPROTO_IPV6: ip6_forward(m1, 0); break; - case IPPROTO_MAX: /* discard this packet */ - default: - } - - if (m != m1) - m_freem(m); - - return; + if ((m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) != 0) { + if (IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_dst)) { + ours = 1; + deliverifp = m->m_pkthdr.rcvif; + goto hbhcheck; + } } - mcastcheck: -#endif - /* * Multicast check */ if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { struct in6_multi *in6m = 0; in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_mcast); /* * See if we belong to the destination multicast group on the * arrival interface. */ IN6_LOOKUP_MULTI(ip6->ip6_dst, m->m_pkthdr.rcvif, in6m); if (in6m) ours = 1; else if (!ip6_mrouter) { ip6stat.ip6s_notmember++; ip6stat.ip6s_cantforward++; in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_discard); goto bad; } deliverifp = m->m_pkthdr.rcvif; goto hbhcheck; } /* * Unicast check */ - if (ip6_forward_rt.ro_rt == 0 || - !IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, - &ip6_forward_rt.ro_dst.sin6_addr)) { + if (ip6_forward_rt.ro_rt != NULL && + (ip6_forward_rt.ro_rt->rt_flags & RTF_UP) != 0 && + IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, + &ip6_forward_rt.ro_dst.sin6_addr)) + ip6stat.ip6s_forward_cachehit++; + else { if (ip6_forward_rt.ro_rt) { + /* route is down or destination is different */ + ip6stat.ip6s_forward_cachemiss++; RTFREE(ip6_forward_rt.ro_rt); ip6_forward_rt.ro_rt = 0; } + bzero(&ip6_forward_rt.ro_dst, sizeof(struct sockaddr_in6)); ip6_forward_rt.ro_dst.sin6_len = sizeof(struct sockaddr_in6); ip6_forward_rt.ro_dst.sin6_family = AF_INET6; ip6_forward_rt.ro_dst.sin6_addr = ip6->ip6_dst; +#ifdef SCOPEDROUTING + ip6_forward_rt.ro_dst.sin6_scope_id = + in6_addr2scopeid(m->m_pkthdr.rcvif, &ip6->ip6_dst); +#endif rtalloc_ign((struct route *)&ip6_forward_rt, RTF_PRCLONING); } #define rt6_key(r) ((struct sockaddr_in6 *)((r)->rt_nodes->rn_key)) /* * Accept the packet if the forwarding interface to the destination * according to the routing table is the loopback interface, * unless the associated route has a gateway. * Note that this approach causes to accept a packet if there is a * route to the loopback interface for the destination of the packet. * But we think it's even useful in some situations, e.g. when using * a special daemon which wants to intercept the packet. */ if (ip6_forward_rt.ro_rt && (ip6_forward_rt.ro_rt->rt_flags & (RTF_HOST|RTF_GATEWAY)) == RTF_HOST && +#if 0 /* - * The comparison of the destination and the key of the rtentry - * has already done through looking up the routing table, - * so no need to do such a comparison here again. + * The check below is redundant since the comparison of + * the destination and the key of the rtentry has + * already done through looking up the routing table. */ + IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, + &rt6_key(ip6_forward_rt.ro_rt)->sin6_addr) && +#endif ip6_forward_rt.ro_rt->rt_ifp->if_type == IFT_LOOP) { struct in6_ifaddr *ia6 = (struct in6_ifaddr *)ip6_forward_rt.ro_rt->rt_ifa; - /* packet to tentative address must not be received */ if (ia6->ia6_flags & IN6_IFF_ANYCAST) m->m_flags |= M_ANYCAST6; + /* + * packets to a tentative, duplicated, or somehow invalid + * address must not be accepted. + */ if (!(ia6->ia6_flags & IN6_IFF_NOTREADY)) { - /* this interface is ready */ + /* this address is ready */ ours = 1; deliverifp = ia6->ia_ifp; /* correct? */ goto hbhcheck; } else { - /* this interface is not ready, fall through */ + /* address is not ready, so discard the packet. */ + log(LOG_INFO, + "ip6_input: packet to an unready address %s->%s", + ip6_sprintf(&ip6->ip6_src), + ip6_sprintf(&ip6->ip6_dst)); + + goto bad; } } /* * FAITH(Firewall Aided Internet Translator) */ #if defined(NFAITH) && 0 < NFAITH if (ip6_keepfaith) { if (ip6_forward_rt.ro_rt && ip6_forward_rt.ro_rt->rt_ifp && ip6_forward_rt.ro_rt->rt_ifp->if_type == IFT_FAITH) { /* XXX do we need more sanity checks? */ ours = 1; deliverifp = ip6_forward_rt.ro_rt->rt_ifp; /*faith*/ goto hbhcheck; } } #endif /* * Now there is no reason to process the packet if it's not our own * and we're not a router. */ if (!ip6_forwarding) { ip6stat.ip6s_cantforward++; in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_discard); goto bad; } hbhcheck: /* * Process Hop-by-Hop options header if it's contained. * m may be modified in ip6_hopopts_input(). * If a JumboPayload option is included, plen will also be modified. */ plen = (u_int32_t)ntohs(ip6->ip6_plen); if (ip6->ip6_nxt == IPPROTO_HOPOPTS) { + struct ip6_hbh *hbh; + if (ip6_hopopts_input(&plen, &rtalert, &m, &off)) { +#if 0 /*touches NULL pointer*/ in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_discard); +#endif return; /* m have already been freed */ } + /* adjust pointer */ ip6 = mtod(m, struct ip6_hdr *); - nxt = ((struct ip6_hbh *)(ip6 + 1))->ip6h_nxt; /* + * if the payload length field is 0 and the next header field + * indicates Hop-by-Hop Options header, then a Jumbo Payload + * option MUST be included. + */ + if (ip6->ip6_plen == 0 && plen == 0) { + /* + * Note that if a valid jumbo payload option is + * contained, ip6_hoptops_input() must set a valid + * (non-zero) payload length to the variable plen. + */ + ip6stat.ip6s_badoptions++; + in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_discard); + in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_hdrerr); + icmp6_error(m, ICMP6_PARAM_PROB, + ICMP6_PARAMPROB_HEADER, + (caddr_t)&ip6->ip6_plen - (caddr_t)ip6); + return; + } +#ifndef PULLDOWN_TEST + /* ip6_hopopts_input() ensures that mbuf is contiguous */ + hbh = (struct ip6_hbh *)(ip6 + 1); +#else + IP6_EXTHDR_GET(hbh, struct ip6_hbh *, m, sizeof(struct ip6_hdr), + sizeof(struct ip6_hbh)); + if (hbh == NULL) { + ip6stat.ip6s_tooshort++; + return; + } +#endif + nxt = hbh->ip6h_nxt; + + /* * accept the packet if a router alert option is included * and we act as an IPv6 router. */ if (rtalert != ~0 && ip6_forwarding) ours = 1; } else nxt = ip6->ip6_nxt; /* * Check that the amount of data in the buffers * is as at least much as the IPv6 header would have us expect. * Trim mbufs if longer than we expect. * Drop packet if shorter than we expect. */ if (m->m_pkthdr.len - sizeof(struct ip6_hdr) < plen) { ip6stat.ip6s_tooshort++; in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_truncated); goto bad; } if (m->m_pkthdr.len > sizeof(struct ip6_hdr) + plen) { if (m->m_len == m->m_pkthdr.len) { m->m_len = sizeof(struct ip6_hdr) + plen; m->m_pkthdr.len = sizeof(struct ip6_hdr) + plen; } else m_adj(m, sizeof(struct ip6_hdr) + plen - m->m_pkthdr.len); } /* * Forward if desirable. */ if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { /* * If we are acting as a multicast router, all * incoming multicast packets are passed to the * kernel-level multicast forwarding function. * The packet is returned (relatively) intact; if * ip6_mforward() returns a non-zero value, the packet * must be discarded, else it may be accepted below. */ if (ip6_mrouter && ip6_mforward(ip6, m->m_pkthdr.rcvif, m)) { ip6stat.ip6s_cantforward++; m_freem(m); return; } if (!ours) { m_freem(m); return; } } else if (!ours) { ip6_forward(m, 0); return; } + ip6 = mtod(m, struct ip6_hdr *); + /* - * Tell launch routine the next header + * Malicious party may be able to use IPv4 mapped addr to confuse + * tcp/udp stack and bypass security checks (act as if it was from + * 127.0.0.1 by using IPv6 src ::ffff:127.0.0.1). Be cautious. + * + * For SIIT end node behavior, you may want to disable the check. + * However, you will become vulnerable to attacks using IPv4 mapped + * source. */ -#if defined(__NetBSD__) && defined(IFA_STATS) - if (IFA_STATS && deliverifp != NULL) { - struct in6_ifaddr *ia6; - ip6 = mtod(m, struct ip6_hdr *); - ia6 = in6_ifawithifp(deliverifp, &ip6->ip6_dst); - if (ia6) - ia6->ia_ifa.ifa_data.ifad_inbytes += m->m_pkthdr.len; + if (IN6_IS_ADDR_V4MAPPED(&ip6->ip6_src) || + IN6_IS_ADDR_V4MAPPED(&ip6->ip6_dst)) { + ip6stat.ip6s_badscope++; + in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_addrerr); + goto bad; } -#endif + + /* + * Tell launch routine the next header + */ ip6stat.ip6s_delivered++; in6_ifstat_inc(deliverifp, ifs6_in_deliver); nest = 0; while (nxt != IPPROTO_DONE) { if (ip6_hdrnestlimit && (++nest > ip6_hdrnestlimit)) { ip6stat.ip6s_toomanyhdr++; goto bad; } /* * protection against faulty packet - there should be * more sanity checks in header chain processing. */ if (m->m_pkthdr.len < off) { ip6stat.ip6s_tooshort++; in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_truncated); goto bad; } nxt = (*inet6sw[ip6_protox[nxt]].pr_input)(&m, &off, nxt); } return; bad: m_freem(m); } /* * Hop-by-Hop options header processing. If a valid jumbo payload option is * included, the real payload length will be stored in plenp. */ static int ip6_hopopts_input(plenp, rtalertp, mp, offp) u_int32_t *plenp; u_int32_t *rtalertp; /* XXX: should be stored more smart way */ struct mbuf **mp; int *offp; { register struct mbuf *m = *mp; int off = *offp, hbhlen; struct ip6_hbh *hbh; u_int8_t *opt; /* validation of the length of the header */ +#ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, off, sizeof(*hbh), -1); hbh = (struct ip6_hbh *)(mtod(m, caddr_t) + off); hbhlen = (hbh->ip6h_len + 1) << 3; IP6_EXTHDR_CHECK(m, off, hbhlen, -1); hbh = (struct ip6_hbh *)(mtod(m, caddr_t) + off); +#else + IP6_EXTHDR_GET(hbh, struct ip6_hbh *, m, + sizeof(struct ip6_hdr), sizeof(struct ip6_hbh)); + if (hbh == NULL) { + ip6stat.ip6s_tooshort++; + return -1; + } + hbhlen = (hbh->ip6h_len + 1) << 3; + IP6_EXTHDR_GET(hbh, struct ip6_hbh *, m, sizeof(struct ip6_hdr), + hbhlen); + if (hbh == NULL) { + ip6stat.ip6s_tooshort++; + return -1; + } +#endif off += hbhlen; hbhlen -= sizeof(struct ip6_hbh); opt = (u_int8_t *)hbh + sizeof(struct ip6_hbh); if (ip6_process_hopopts(m, (u_int8_t *)hbh + sizeof(struct ip6_hbh), hbhlen, rtalertp, plenp) < 0) return(-1); *offp = off; *mp = m; return(0); } /* * Search header for all Hop-by-hop options and process each option. * This function is separate from ip6_hopopts_input() in order to * handle a case where the sending node itself process its hop-by-hop * options header. In such a case, the function is called from ip6_output(). */ int ip6_process_hopopts(m, opthead, hbhlen, rtalertp, plenp) struct mbuf *m; u_int8_t *opthead; int hbhlen; u_int32_t *rtalertp; u_int32_t *plenp; { struct ip6_hdr *ip6; int optlen = 0; u_int8_t *opt = opthead; u_int16_t rtalert_val; + u_int32_t jumboplen; for (; hbhlen > 0; hbhlen -= optlen, opt += optlen) { switch(*opt) { case IP6OPT_PAD1: optlen = 1; break; case IP6OPT_PADN: if (hbhlen < IP6OPT_MINLEN) { ip6stat.ip6s_toosmall++; goto bad; } optlen = *(opt + 1) + 2; break; case IP6OPT_RTALERT: /* XXX may need check for alignment */ if (hbhlen < IP6OPT_RTALERT_LEN) { ip6stat.ip6s_toosmall++; goto bad; } if (*(opt + 1) != IP6OPT_RTALERT_LEN - 2) /* XXX: should we discard the packet? */ log(LOG_ERR, "length of router alert opt is inconsitent(%d)", *(opt + 1)); optlen = IP6OPT_RTALERT_LEN; bcopy((caddr_t)(opt + 2), (caddr_t)&rtalert_val, 2); *rtalertp = ntohs(rtalert_val); break; case IP6OPT_JUMBO: - /* XXX may need check for alignment */ - if (hbhlen < IP6OPT_JUMBO_LEN) { - ip6stat.ip6s_toosmall++; - goto bad; - } - if (*(opt + 1) != IP6OPT_JUMBO_LEN - 2) - /* XXX: should we discard the packet? */ - log(LOG_ERR, "length of jumbopayload opt " - "is inconsistent(%d)", - *(opt + 1)); - optlen = IP6OPT_JUMBO_LEN; + /* XXX may need check for alignment */ + if (hbhlen < IP6OPT_JUMBO_LEN) { + ip6stat.ip6s_toosmall++; + goto bad; + } + if (*(opt + 1) != IP6OPT_JUMBO_LEN - 2) + /* XXX: should we discard the packet? */ + log(LOG_ERR, "length of jumbopayload opt " + "is inconsistent(%d)", + *(opt + 1)); + optlen = IP6OPT_JUMBO_LEN; - bcopy(opt + 2, plenp, sizeof(*plenp)); - *plenp = htonl(*plenp); - if (*plenp <= IPV6_MAXPACKET) { - /* - * jumbo payload length must be larger - * than 65535 - */ - ip6stat.ip6s_badoptions++; - icmp6_error(m, ICMP6_PARAM_PROB, - ICMP6_PARAMPROB_HEADER, - sizeof(struct ip6_hdr) + - sizeof(struct ip6_hbh) + - opt + 2 - opthead); - return(-1); - } + /* + * IPv6 packets that have non 0 payload length + * must not contain a jumbo paylod option. + */ + ip6 = mtod(m, struct ip6_hdr *); + if (ip6->ip6_plen) { + ip6stat.ip6s_badoptions++; + icmp6_error(m, ICMP6_PARAM_PROB, + ICMP6_PARAMPROB_HEADER, + sizeof(struct ip6_hdr) + + sizeof(struct ip6_hbh) + + opt - opthead); + return(-1); + } - ip6 = mtod(m, struct ip6_hdr *); - if (ip6->ip6_plen) { - /* - * IPv6 packets that have non 0 payload length - * must not contain a jumbo paylod option. - */ - ip6stat.ip6s_badoptions++; - icmp6_error(m, ICMP6_PARAM_PROB, - ICMP6_PARAMPROB_HEADER, - sizeof(struct ip6_hdr) + - sizeof(struct ip6_hbh) + - opt - opthead); - return(-1); - } - break; + /* + * We may see jumbolen in unaligned location, so + * we'd need to perform bcopy(). + */ + bcopy(opt + 2, &jumboplen, sizeof(jumboplen)); + jumboplen = (u_int32_t)htonl(jumboplen); + +#if 1 + /* + * if there are multiple jumbo payload options, + * *plenp will be non-zero and the packet will be + * rejected. + * the behavior may need some debate in ipngwg - + * multiple options does not make sense, however, + * there's no explicit mention in specification. + */ + if (*plenp != 0) { + ip6stat.ip6s_badoptions++; + icmp6_error(m, ICMP6_PARAM_PROB, + ICMP6_PARAMPROB_HEADER, + sizeof(struct ip6_hdr) + + sizeof(struct ip6_hbh) + + opt + 2 - opthead); + return(-1); + } +#endif + + /* + * jumbo payload length must be larger than 65535. + */ + if (jumboplen <= IPV6_MAXPACKET) { + ip6stat.ip6s_badoptions++; + icmp6_error(m, ICMP6_PARAM_PROB, + ICMP6_PARAMPROB_HEADER, + sizeof(struct ip6_hdr) + + sizeof(struct ip6_hbh) + + opt + 2 - opthead); + return(-1); + } + *plenp = jumboplen; + + break; default: /* unknown option */ if (hbhlen < IP6OPT_MINLEN) { ip6stat.ip6s_toosmall++; goto bad; } if ((optlen = ip6_unknown_opt(opt, m, sizeof(struct ip6_hdr) + sizeof(struct ip6_hbh) + opt - opthead)) == -1) return(-1); optlen += 2; break; } } return(0); bad: m_freem(m); return(-1); } /* * Unknown option processing. * The third argument `off' is the offset from the IPv6 header to the option, * which is necessary if the IPv6 header the and option header and IPv6 header * is not continuous in order to return an ICMPv6 error. */ int ip6_unknown_opt(optp, m, off) u_int8_t *optp; struct mbuf *m; int off; { struct ip6_hdr *ip6; switch(IP6OPT_TYPE(*optp)) { case IP6OPT_TYPE_SKIP: /* ignore the option */ return((int)*(optp + 1)); case IP6OPT_TYPE_DISCARD: /* silently discard */ m_freem(m); return(-1); case IP6OPT_TYPE_FORCEICMP: /* send ICMP even if multicasted */ ip6stat.ip6s_badoptions++; icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_OPTION, off); return(-1); case IP6OPT_TYPE_ICMP: /* send ICMP if not multicasted */ ip6stat.ip6s_badoptions++; ip6 = mtod(m, struct ip6_hdr *); if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) || (m->m_flags & (M_BCAST|M_MCAST))) m_freem(m); else icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_OPTION, off); return(-1); } m_freem(m); /* XXX: NOTREACHED */ return(-1); } /* - * Create the "control" list for this pcb + * Create the "control" list for this pcb. + * + * The routine will be called from upper layer handlers like tcp6_input(). + * Thus the routine assumes that the caller (tcp6_input) have already + * called IP6_EXTHDR_CHECK() and all the extension headers are located in the + * very first mbuf on the mbuf chain. + * We may want to add some infinite loop prevention or sanity checks for safety. + * (This applies only when you are using KAME mbuf chain restriction, i.e. + * you are using IP6_EXTHDR_CHECK() not m_pulldown()) */ void ip6_savecontrol(in6p, mp, ip6, m) - register struct inpcb *in6p; + register struct in6pcb *in6p; register struct mbuf **mp; register struct ip6_hdr *ip6; register struct mbuf *m; { struct proc *p = curproc; /* XXX */ int privileged; privileged = 0; if (p && !suser(p)) privileged++; if (in6p->in6p_socket->so_options & SO_TIMESTAMP) { struct timeval tv; microtime(&tv); *mp = sbcreatecontrol((caddr_t) &tv, sizeof(tv), SCM_TIMESTAMP, SOL_SOCKET); if (*mp) mp = &(*mp)->m_next; } - if (in6p->in6p_flags & IN6P_RECVDSTADDR) { - *mp = sbcreatecontrol((caddr_t) &ip6->ip6_dst, - sizeof(struct in6_addr), IPV6_RECVDSTADDR, - IPPROTO_IPV6); - if (*mp) - mp = &(*mp)->m_next; - } +#ifdef noyet + /* options were tossed above */ + if (in6p->in6p_flags & IN6P_RECVOPTS) + /* broken */ + /* ip6_srcroute doesn't do what we want here, need to fix */ + if (in6p->in6p_flags & IPV6P_RECVRETOPTS) + /* broken */ +#endif + /* RFC 2292 sec. 5 */ if (in6p->in6p_flags & IN6P_PKTINFO) { struct in6_pktinfo pi6; bcopy(&ip6->ip6_dst, &pi6.ipi6_addr, sizeof(struct in6_addr)); if (IN6_IS_SCOPE_LINKLOCAL(&pi6.ipi6_addr)) pi6.ipi6_addr.s6_addr16[1] = 0; pi6.ipi6_ifindex = (m && m->m_pkthdr.rcvif) ? m->m_pkthdr.rcvif->if_index : 0; *mp = sbcreatecontrol((caddr_t) &pi6, sizeof(struct in6_pktinfo), IPV6_PKTINFO, IPPROTO_IPV6); if (*mp) mp = &(*mp)->m_next; } if (in6p->in6p_flags & IN6P_HOPLIMIT) { int hlim = ip6->ip6_hlim & 0xff; *mp = sbcreatecontrol((caddr_t) &hlim, sizeof(int), IPV6_HOPLIMIT, IPPROTO_IPV6); if (*mp) mp = &(*mp)->m_next; } /* IN6P_NEXTHOP - for outgoing packet only */ /* * IPV6_HOPOPTS socket option. We require super-user privilege * for the option, but it might be too strict, since there might * be some hop-by-hop options which can be returned to normal user. * See RFC 2292 section 6. */ if ((in6p->in6p_flags & IN6P_HOPOPTS) && privileged) { /* * Check if a hop-by-hop options header is contatined in the * received packet, and if so, store the options as ancillary * data. Note that a hop-by-hop options header must be * just after the IPv6 header, which fact is assured through * the IPv6 input processing. */ struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); if (ip6->ip6_nxt == IPPROTO_HOPOPTS) { - struct ip6_hbh *hbh = (struct ip6_hbh *)(ip6 + 1); + struct ip6_hbh *hbh; + int hbhlen; +#ifndef PULLDOWN_TEST + hbh = (struct ip6_hbh *)(ip6 + 1); + hbhlen = (hbh->ip6h_len + 1) << 3; +#else + IP6_EXTHDR_GET(hbh, struct ip6_hbh *, m, + sizeof(struct ip6_hdr), sizeof(struct ip6_hbh)); + if (hbh == NULL) { + ip6stat.ip6s_tooshort++; + return; + } + hbhlen = (hbh->ip6h_len + 1) << 3; + IP6_EXTHDR_GET(hbh, struct ip6_hbh *, m, + sizeof(struct ip6_hdr), hbhlen); + if (hbh == NULL) { + ip6stat.ip6s_tooshort++; + return; + } +#endif + /* * XXX: We copy whole the header even if a jumbo * payload option is included, which option is to * be removed before returning in the RFC 2292. * But it's too painful operation... */ - *mp = sbcreatecontrol((caddr_t)hbh, - (hbh->ip6h_len + 1) << 3, + *mp = sbcreatecontrol((caddr_t)hbh, hbhlen, IPV6_HOPOPTS, IPPROTO_IPV6); if (*mp) mp = &(*mp)->m_next; } } /* IPV6_DSTOPTS and IPV6_RTHDR socket options */ if (in6p->in6p_flags & (IN6P_DSTOPTS | IN6P_RTHDR)) { struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); int nxt = ip6->ip6_nxt, off = sizeof(struct ip6_hdr);; /* * Search for destination options headers or routing * header(s) through the header chain, and stores each * header as ancillary data. * Note that the order of the headers remains in * the chain of ancillary data. */ while(1) { /* is explicit loop prevention necessary? */ - struct ip6_ext *ip6e = - (struct ip6_ext *)(mtod(m, caddr_t) + off); + struct ip6_ext *ip6e; + int elen; +#ifndef PULLDOWN_TEST + ip6e = (struct ip6_ext *)(mtod(m, caddr_t) + off); + if (nxt == IPPROTO_AH) + elen = (ip6e->ip6e_len + 2) << 2; + else + elen = (ip6e->ip6e_len + 1) << 3; +#else + IP6_EXTHDR_GET(ip6e, struct ip6_ext *, m, off, + sizeof(struct ip6_ext)); + if (ip6e == NULL) { + ip6stat.ip6s_tooshort++; + return; + } + if (nxt == IPPROTO_AH) + elen = (ip6e->ip6e_len + 2) << 2; + else + elen = (ip6e->ip6e_len + 1) << 3; + IP6_EXTHDR_GET(ip6e, struct ip6_ext *, m, off, elen); + if (ip6e == NULL) { + ip6stat.ip6s_tooshort++; + return; + } +#endif + switch(nxt) { case IPPROTO_DSTOPTS: if (!in6p->in6p_flags & IN6P_DSTOPTS) break; /* * We also require super-user privilege for * the option. * See the comments on IN6_HOPOPTS. */ if (!privileged) break; - *mp = sbcreatecontrol((caddr_t)ip6e, - (ip6e->ip6e_len + 1) << 3, + *mp = sbcreatecontrol((caddr_t)ip6e, elen, IPV6_DSTOPTS, IPPROTO_IPV6); if (*mp) mp = &(*mp)->m_next; break; case IPPROTO_ROUTING: if (!in6p->in6p_flags & IN6P_RTHDR) break; - *mp = sbcreatecontrol((caddr_t)ip6e, - (ip6e->ip6e_len + 1) << 3, + *mp = sbcreatecontrol((caddr_t)ip6e, elen, IPV6_RTHDR, IPPROTO_IPV6); if (*mp) mp = &(*mp)->m_next; break; case IPPROTO_UDP: case IPPROTO_TCP: case IPPROTO_ICMPV6: default: /* * stop search if we encounter an upper * layer protocol headers. */ goto loopend; case IPPROTO_HOPOPTS: case IPPROTO_AH: /* is it possible? */ break; } /* proceed with the next header. */ - if (nxt == IPPROTO_AH) - off += (ip6e->ip6e_len + 2) << 2; - else - off += (ip6e->ip6e_len + 1) << 3; + off += elen; nxt = ip6e->ip6e_nxt; } loopend: } if ((in6p->in6p_flags & IN6P_HOPOPTS) && privileged) { /* to be done */ } if ((in6p->in6p_flags & IN6P_DSTOPTS) && privileged) { /* to be done */ } /* IN6P_RTHDR - to be done */ + } /* * Get pointer to the previous header followed by the header * currently processed. * XXX: This function supposes that * M includes all headers, * the next header field and the header length field of each header * are valid, and * the sum of each header length equals to OFF. * Because of these assumptions, this function must be called very * carefully. Moreover, it will not be used in the near future when * we develop `neater' mechanism to process extension headers. */ char * ip6_get_prevhdr(m, off) struct mbuf *m; int off; { struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); if (off == sizeof(struct ip6_hdr)) return(&ip6->ip6_nxt); else { int len, nxt; struct ip6_ext *ip6e = NULL; nxt = ip6->ip6_nxt; len = sizeof(struct ip6_hdr); while (len < off) { ip6e = (struct ip6_ext *)(mtod(m, caddr_t) + len); switch(nxt) { case IPPROTO_FRAGMENT: len += sizeof(struct ip6_frag); break; case IPPROTO_AH: len += (ip6e->ip6e_len + 2) << 2; break; default: len += (ip6e->ip6e_len + 1) << 3; break; } nxt = ip6e->ip6e_nxt; } if (ip6e) return(&ip6e->ip6e_nxt); else return NULL; + } +} + +/* + * get next header offset. m will be retained. + */ +int +ip6_nexthdr(m, off, proto, nxtp) + struct mbuf *m; + int off; + int proto; + int *nxtp; +{ + struct ip6_hdr ip6; + struct ip6_ext ip6e; + struct ip6_frag fh; + + /* just in case */ + if (m == NULL) + panic("ip6_nexthdr: m == NULL"); + if ((m->m_flags & M_PKTHDR) == 0 || m->m_pkthdr.len < off) + return -1; + + switch (proto) { + case IPPROTO_IPV6: + if (m->m_pkthdr.len < off + sizeof(ip6)) + return -1; + m_copydata(m, off, sizeof(ip6), (caddr_t)&ip6); + if (nxtp) + *nxtp = ip6.ip6_nxt; + off += sizeof(ip6); + return off; + + case IPPROTO_FRAGMENT: + /* + * terminate parsing if it is not the first fragment, + * it does not make sense to parse through it. + */ + if (m->m_pkthdr.len < off + sizeof(fh)) + return -1; + m_copydata(m, off, sizeof(fh), (caddr_t)&fh); + if ((ntohs(fh.ip6f_offlg) & IP6F_OFF_MASK) != 0) + return -1; + if (nxtp) + *nxtp = fh.ip6f_nxt; + off += sizeof(struct ip6_frag); + return off; + + case IPPROTO_AH: + if (m->m_pkthdr.len < off + sizeof(ip6e)) + return -1; + m_copydata(m, off, sizeof(ip6e), (caddr_t)&ip6e); + if (nxtp) + *nxtp = ip6e.ip6e_nxt; + off += (ip6e.ip6e_len + 2) << 2; + return off; + + case IPPROTO_HOPOPTS: + case IPPROTO_ROUTING: + case IPPROTO_DSTOPTS: + if (m->m_pkthdr.len < off + sizeof(ip6e)) + return -1; + m_copydata(m, off, sizeof(ip6e), (caddr_t)&ip6e); + if (nxtp) + *nxtp = ip6e.ip6e_nxt; + off += (ip6e.ip6e_len + 1) << 3; + return off; + + case IPPROTO_NONE: + case IPPROTO_ESP: + case IPPROTO_IPCOMP: + /* give up */ + return -1; + + default: + return -1; + } + + return -1; +} + +/* + * get offset for the last header in the chain. m will be kept untainted. + */ +int +ip6_lasthdr(m, off, proto, nxtp) + struct mbuf *m; + int off; + int proto; + int *nxtp; +{ + int newoff; + int nxt; + + if (!nxtp) { + nxt = -1; + nxtp = &nxt; + } + while (1) { + newoff = ip6_nexthdr(m, off, proto, nxtp); + if (newoff < 0) + return off; + else if (newoff < off) + return -1; /* invalid */ + else if (newoff == off) + return newoff; + + off = newoff; + proto = *nxtp; } } /* * System control for IP6 */ u_char inet6ctlerrmap[PRC_NCMDS] = { 0, 0, 0, 0, 0, EMSGSIZE, EHOSTDOWN, EHOSTUNREACH, EHOSTUNREACH, EHOSTUNREACH, ECONNREFUSED, ECONNREFUSED, EMSGSIZE, EHOSTUNREACH, 0, 0, 0, 0, 0, 0, ENOPROTOOPT }; Index: head/sys/netinet6/ip6_mroute.c =================================================================== --- head/sys/netinet6/ip6_mroute.c (revision 62586) +++ head/sys/netinet6/ip6_mroute.c (revision 62587) @@ -1,1661 +1,1773 @@ +/* $FreeBSD$ */ +/* $KAME: ip6_mroute.c,v 1.24 2000/05/19 07:37:05 jinmei Exp $ */ + /* * Copyright (C) 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* BSDI ip_mroute.c,v 2.10 1996/11/14 00:29:52 jch Exp */ /* * IP multicast forwarding procedures * * Written by David Waitzman, BBN Labs, August 1988. * Modified by Steve Deering, Stanford, February 1989. * Modified by Mark J. Steiglitz, Stanford, May, 1991 * Modified by Van Jacobson, LBL, January 1993 * Modified by Ajit Thyagarajan, PARC, August 1993 * Modified by Bill Fenenr, PARC, April 1994 * * MROUTING Revision: 3.5.1.2 + PIM-SMv2 (pimd) Support */ #include "opt_inet.h" +#include "opt_inet6.h" #include #include #include #include #include #include #include #include #include #include #include -#include #include #include #include #include #include #include -#include +#include #include #include #include #include static MALLOC_DEFINE(M_MRTABLE, "mf6c", "multicast forwarding cache entry"); -#define M_HASCL(m) ((m)->m_flags & M_EXT) +#define M_HASCL(m) ((m)->m_flags & M_EXT) -static int ip6_mdq __P((struct mbuf *, struct ifnet *, struct mf6c *)); -static void phyint_send __P((struct ip6_hdr *, struct mif6 *, - struct mbuf *)); +static int ip6_mdq __P((struct mbuf *, struct ifnet *, struct mf6c *)); +static void phyint_send __P((struct ip6_hdr *, struct mif6 *, struct mbuf *)); -static int set_pim6 __P((int *)); -static int socket_send __P((struct socket *, struct mbuf *, - struct sockaddr_in6 *)); -static int register_send __P((struct ip6_hdr *, struct mif6 *, - struct mbuf *)); +static int set_pim6 __P((int *)); +static int socket_send __P((struct socket *, struct mbuf *, + struct sockaddr_in6 *)); +static int register_send __P((struct ip6_hdr *, struct mif6 *, + struct mbuf *)); /* * Globals. All but ip6_mrouter, ip6_mrtproto and mrt6stat could be static, * except for netstat or debugging purposes. */ -struct socket *ip6_mrouter = NULL; -int ip6_mrtproto = IPPROTO_PIM; /* for netstat only */ -struct mrt6stat mrt6stat; +struct socket *ip6_mrouter = NULL; +int ip6_mrouter_ver = 0; +int ip6_mrtproto = IPPROTO_PIM; /* for netstat only */ +struct mrt6stat mrt6stat; -#define NO_RTE_FOUND 0x1 -#define RTE_FOUND 0x2 +#define NO_RTE_FOUND 0x1 +#define RTE_FOUND 0x2 -struct mf6c *mf6ctable[MF6CTBLSIZ]; -u_char nexpire[MF6CTBLSIZ]; -static struct mif6 mif6table[MAXMIFS]; +struct mf6c *mf6ctable[MF6CTBLSIZ]; +u_char nexpire[MF6CTBLSIZ]; +static struct mif6 mif6table[MAXMIFS]; #ifdef MRT6DEBUG u_int mrt6debug = 0; /* debug level */ -#define DEBUG_MFC 0x02 -#define DEBUG_FORWARD 0x04 -#define DEBUG_EXPIRE 0x08 -#define DEBUG_XMIT 0x10 -#define DEBUG_REG 0x20 -#define DEBUG_PIM 0x40 +#define DEBUG_MFC 0x02 +#define DEBUG_FORWARD 0x04 +#define DEBUG_EXPIRE 0x08 +#define DEBUG_XMIT 0x10 +#define DEBUG_REG 0x20 +#define DEBUG_PIM 0x40 #endif static void expire_upcalls __P((void *)); -#define EXPIRE_TIMEOUT (hz / 4) /* 4x / second */ -#define UPCALL_EXPIRE 6 /* number of timeouts */ +#define EXPIRE_TIMEOUT (hz / 4) /* 4x / second */ +#define UPCALL_EXPIRE 6 /* number of timeouts */ #ifdef INET #ifdef MROUTING extern struct socket *ip_mrouter; #endif #endif /* * 'Interfaces' associated with decapsulator (so we can tell * packets that went through it from ones that get reflected * by a broken gateway). These interfaces are never linked into * the system ifnet list & no routes point to them. I.e., packets * can't be sent this way. They only exist as a placeholder for * multicast source verification. */ struct ifnet multicast_register_if; -#define ENCAP_HOPS 64 +#define ENCAP_HOPS 64 /* * Private variables. */ static mifi_t nummifs = 0; static mifi_t reg_mif_num = (mifi_t)-1; static struct pim6stat pim6stat; static struct callout_handle expire_upcalls_ch; /* * one-back cache used by ipip_input to locate a tunnel's mif * given a datagram's src ip address. */ static int pim6; /* * Hash function for a source, group entry */ -#define MF6CHASH(a, g) MF6CHASHMOD((a).s6_addr32[0] ^ (a).s6_addr32[1] ^ \ +#define MF6CHASH(a, g) MF6CHASHMOD((a).s6_addr32[0] ^ (a).s6_addr32[1] ^ \ (a).s6_addr32[2] ^ (a).s6_addr32[3] ^ \ (g).s6_addr32[0] ^ (g).s6_addr32[1] ^ \ (g).s6_addr32[2] ^ (g).s6_addr32[3]) /* * Find a route for a given origin IPv6 address and Multicast group address. * Quality of service parameter to be added in the future!!! */ -#define MF6CFIND(o, g, rt) { \ +#define MF6CFIND(o, g, rt) do { \ register struct mf6c *_rt = mf6ctable[MF6CHASH(o,g)]; \ rt = NULL; \ mrt6stat.mrt6s_mfc_lookups++; \ while (_rt) { \ if (IN6_ARE_ADDR_EQUAL(&_rt->mf6c_origin.sin6_addr, &(o)) && \ IN6_ARE_ADDR_EQUAL(&_rt->mf6c_mcastgrp.sin6_addr, &(g)) && \ (_rt->mf6c_stall == NULL)) { \ rt = _rt; \ break; \ } \ _rt = _rt->mf6c_next; \ } \ if (rt == NULL) { \ mrt6stat.mrt6s_mfc_misses++; \ } \ -} +} while (0) /* * Macros to compute elapsed time efficiently * Borrowed from Van Jacobson's scheduling code */ -#define TV_DELTA(a, b, delta) { \ +#define TV_DELTA(a, b, delta) do { \ register int xxs; \ \ delta = (a).tv_usec - (b).tv_usec; \ if ((xxs = (a).tv_sec - (b).tv_sec)) { \ switch (xxs) { \ case 2: \ delta += 1000000; \ /* fall through */ \ case 1: \ delta += 1000000; \ break; \ default: \ delta += (1000000 * xxs); \ } \ } \ -} +} while (0) -#define TV_LT(a, b) (((a).tv_usec < (b).tv_usec && \ +#define TV_LT(a, b) (((a).tv_usec < (b).tv_usec && \ (a).tv_sec <= (b).tv_sec) || (a).tv_sec < (b).tv_sec) #ifdef UPCALL_TIMING -#define UPCALL_MAX 50 +#define UPCALL_MAX 50 u_long upcall_data[UPCALL_MAX + 1]; static void collate(); #endif /* UPCALL_TIMING */ static int get_sg_cnt __P((struct sioc_sg_req6 *)); static int get_mif6_cnt __P((struct sioc_mif_req6 *)); -static int ip6_mrouter_init __P((struct socket *, struct mbuf *)); +static int ip6_mrouter_init __P((struct socket *, struct mbuf *, int)); static int add_m6if __P((struct mif6ctl *)); static int del_m6if __P((mifi_t *)); static int add_m6fc __P((struct mf6cctl *)); static int del_m6fc __P((struct mf6cctl *)); /* * Handle MRT setsockopt commands to modify the multicast routing tables. */ int ip6_mrouter_set(so, sopt) struct socket *so; struct sockopt *sopt; { int error = 0; struct mbuf *m; if (so != ip6_mrouter && sopt->sopt_name != MRT6_INIT) return (EACCES); if ((error = soopt_getm(sopt, &m)) != 0) /* XXX */ return (error); if ((error = soopt_mcopyin(sopt, m)) != 0) /* XXX */ return (error); switch (sopt->sopt_name) { case MRT6_INIT: - error = ip6_mrouter_init(so, m); +#ifdef MRT6_OINIT + case MRT6_OINIT: +#endif + error = ip6_mrouter_init(so, m, sopt->sopt_name); break; case MRT6_DONE: error = ip6_mrouter_done(); break; case MRT6_ADD_MIF: error = add_m6if(mtod(m, struct mif6ctl *)); break; case MRT6_DEL_MIF: error = del_m6if(mtod(m, mifi_t *)); break; case MRT6_ADD_MFC: error = add_m6fc(mtod(m, struct mf6cctl *)); break; case MRT6_DEL_MFC: error = del_m6fc(mtod(m, struct mf6cctl *)); break; case MRT6_PIM: error = set_pim6(mtod(m, int *)); break; default: error = EOPNOTSUPP; break; } (void)m_freem(m); return(error); } /* * Handle MRT getsockopt commands */ int ip6_mrouter_get(so, sopt) struct socket *so; struct sockopt *sopt; { int error = 0; if (so != ip6_mrouter) return EACCES; switch (sopt->sopt_name) { case MRT6_PIM: error = sooptcopyout(sopt, &pim6, sizeof(pim6)); break; } return (error); } /* * Handle ioctl commands to obtain information from the cache */ int mrt6_ioctl(cmd, data) int cmd; caddr_t data; { int error = 0; switch (cmd) { case SIOCGETSGCNT_IN6: return(get_sg_cnt((struct sioc_sg_req6 *)data)); break; /* for safety */ case SIOCGETMIFCNT_IN6: return(get_mif6_cnt((struct sioc_mif_req6 *)data)); break; /* for safety */ default: return (EINVAL); break; } return error; } /* * returns the packet, byte, rpf-failure count for the source group provided */ static int get_sg_cnt(req) register struct sioc_sg_req6 *req; { register struct mf6c *rt; int s; s = splnet(); MF6CFIND(req->src.sin6_addr, req->grp.sin6_addr, rt); splx(s); if (rt != NULL) { req->pktcnt = rt->mf6c_pkt_cnt; req->bytecnt = rt->mf6c_byte_cnt; req->wrong_if = rt->mf6c_wrong_if; } else return(ESRCH); +#if 0 + req->pktcnt = req->bytecnt = req->wrong_if = 0xffffffff; +#endif return 0; } /* * returns the input and output packet and byte counts on the mif provided */ static int get_mif6_cnt(req) register struct sioc_mif_req6 *req; { register mifi_t mifi = req->mifi; if (mifi >= nummifs) return EINVAL; req->icount = mif6table[mifi].m6_pkt_in; req->ocount = mif6table[mifi].m6_pkt_out; req->ibytes = mif6table[mifi].m6_bytes_in; req->obytes = mif6table[mifi].m6_bytes_out; return 0; } static int set_pim6(i) int *i; { if ((*i != 1) && (*i != 0)) return EINVAL; pim6 = *i; return 0; } /* * Enable multicast routing */ static int -ip6_mrouter_init(so, m) +ip6_mrouter_init(so, m, cmd) struct socket *so; struct mbuf *m; + int cmd; { int *v; #ifdef MRT6DEBUG if (mrt6debug) log(LOG_DEBUG, "ip6_mrouter_init: so_type = %d, pr_protocol = %d\n", so->so_type, so->so_proto->pr_protocol); #endif if (so->so_type != SOCK_RAW || so->so_proto->pr_protocol != IPPROTO_ICMPV6) return EOPNOTSUPP; if (!m || (m->m_len != sizeof(int *))) return ENOPROTOOPT; v = mtod(m, int *); if (*v != 1) return ENOPROTOOPT; if (ip6_mrouter != NULL) return EADDRINUSE; ip6_mrouter = so; + ip6_mrouter_ver = cmd; bzero((caddr_t)mf6ctable, sizeof(mf6ctable)); bzero((caddr_t)nexpire, sizeof(nexpire)); pim6 = 0;/* used for stubbing out/in pim stuff */ expire_upcalls_ch = timeout(expire_upcalls, (caddr_t)NULL, EXPIRE_TIMEOUT); #ifdef MRT6DEBUG if (mrt6debug) log(LOG_DEBUG, "ip6_mrouter_init\n"); #endif return 0; } /* * Disable multicast routing */ int ip6_mrouter_done() { mifi_t mifi; int i; struct ifnet *ifp; struct in6_ifreq ifr; struct mf6c *rt; struct rtdetq *rte; int s; s = splnet(); /* * For each phyint in use, disable promiscuous reception of all IPv6 * multicasts. */ #ifdef INET #ifdef MROUTING /* * If there is still IPv4 multicast routing daemon, * we remain interfaces to receive all muliticasted packets. * XXX: there may be an interface in which the IPv4 multicast * daemon is not interested... */ if (!ip_mrouter) #endif #endif { for (mifi = 0; mifi < nummifs; mifi++) { if (mif6table[mifi].m6_ifp && !(mif6table[mifi].m6_flags & MIFF_REGISTER)) { ifr.ifr_addr.sin6_family = AF_INET6; ifr.ifr_addr.sin6_addr= in6addr_any; ifp = mif6table[mifi].m6_ifp; (*ifp->if_ioctl)(ifp, SIOCDELMULTI, (caddr_t)&ifr); } } } +#ifdef notyet + bzero((caddr_t)qtable, sizeof(qtable)); + bzero((caddr_t)tbftable, sizeof(tbftable)); +#endif bzero((caddr_t)mif6table, sizeof(mif6table)); nummifs = 0; pim6 = 0; /* used to stub out/in pim specific code */ untimeout(expire_upcalls, (caddr_t)NULL, expire_upcalls_ch); /* * Free all multicast forwarding cache entries. */ for (i = 0; i < MF6CTBLSIZ; i++) { rt = mf6ctable[i]; while (rt) { struct mf6c *frt; for (rte = rt->mf6c_stall; rte != NULL; ) { struct rtdetq *n = rte->next; m_free(rte->m); free(rte, M_MRTABLE); rte = n; } frt = rt; rt = rt->mf6c_next; free(frt, M_MRTABLE); } } bzero((caddr_t)mf6ctable, sizeof(mf6ctable)); /* * Reset de-encapsulation cache */ reg_mif_num = -1; ip6_mrouter = NULL; + ip6_mrouter_ver = 0; splx(s); #ifdef MRT6DEBUG if (mrt6debug) log(LOG_DEBUG, "ip6_mrouter_done\n"); #endif return 0; } static struct sockaddr_in6 sin6 = { sizeof(sin6), AF_INET6 }; /* * Add a mif to the mif table */ static int add_m6if(mifcp) register struct mif6ctl *mifcp; { register struct mif6 *mifp; struct ifnet *ifp; int error, s; +#ifdef notyet + struct tbf *m_tbf = tbftable + mifcp->mif6c_mifi; +#endif if (mifcp->mif6c_mifi >= MAXMIFS) return EINVAL; mifp = mif6table + mifcp->mif6c_mifi; if (mifp->m6_ifp) return EADDRINUSE; /* XXX: is it appropriate? */ if (mifcp->mif6c_pifi == 0 || mifcp->mif6c_pifi > if_index) return ENXIO; ifp = ifindex2ifnet[mifcp->mif6c_pifi]; if (mifcp->mif6c_flags & MIFF_REGISTER) { if (reg_mif_num == (mifi_t)-1) { multicast_register_if.if_name = "register_mif"; multicast_register_if.if_flags |= IFF_LOOPBACK; multicast_register_if.if_index = mifcp->mif6c_mifi; reg_mif_num = mifcp->mif6c_mifi; } ifp = &multicast_register_if; } /* if REGISTER */ else { /* Make sure the interface supports multicast */ if ((ifp->if_flags & IFF_MULTICAST) == 0) return EOPNOTSUPP; s = splnet(); error = if_allmulti(ifp, 1); splx(s); if (error) return error; } s = splnet(); mifp->m6_flags = mifcp->mif6c_flags; mifp->m6_ifp = ifp; +#ifdef notyet + /* scaling up here allows division by 1024 in critical code */ + mifp->m6_rate_limit = mifcp->mif6c_rate_limit * 1024 / 1000; +#endif /* initialize per mif pkt counters */ mifp->m6_pkt_in = 0; mifp->m6_pkt_out = 0; mifp->m6_bytes_in = 0; mifp->m6_bytes_out = 0; splx(s); /* Adjust nummifs up if the mifi is higher than nummifs */ if (nummifs <= mifcp->mif6c_mifi) nummifs = mifcp->mif6c_mifi + 1; #ifdef MRT6DEBUG if (mrt6debug) log(LOG_DEBUG, "add_mif #%d, phyint %s%d\n", mifcp->mif6c_mifi, ifp->if_name, ifp->if_unit); #endif return 0; } /* * Delete a mif from the mif table */ static int del_m6if(mifip) mifi_t *mifip; { register struct mif6 *mifp = mif6table + *mifip; register mifi_t mifi; struct ifnet *ifp; int s; if (*mifip >= nummifs) return EINVAL; if (mifp->m6_ifp == NULL) return EINVAL; s = splnet(); if (!(mifp->m6_flags & MIFF_REGISTER)) { /* * XXX: what if there is yet IPv4 multicast daemon * using the interface? */ ifp = mifp->m6_ifp; if_allmulti(ifp, 0); } +#ifdef notyet + bzero((caddr_t)qtable[*mifip], sizeof(qtable[*mifip])); + bzero((caddr_t)mifp->m6_tbf, sizeof(*(mifp->m6_tbf))); +#endif bzero((caddr_t)mifp, sizeof (*mifp)); /* Adjust nummifs down */ for (mifi = nummifs; mifi > 0; mifi--) if (mif6table[mifi - 1].m6_ifp) break; nummifs = mifi; splx(s); #ifdef MRT6DEBUG if (mrt6debug) log(LOG_DEBUG, "del_m6if %d, nummifs %d\n", *mifip, nummifs); #endif return 0; } /* * Add an mfc entry */ static int add_m6fc(mfccp) struct mf6cctl *mfccp; { struct mf6c *rt; u_long hash; struct rtdetq *rte; register u_short nstl; int s; MF6CFIND(mfccp->mf6cc_origin.sin6_addr, mfccp->mf6cc_mcastgrp.sin6_addr, rt); /* If an entry already exists, just update the fields */ if (rt) { #ifdef MRT6DEBUG if (mrt6debug & DEBUG_MFC) log(LOG_DEBUG,"add_m6fc update o %s g %s p %x\n", ip6_sprintf(&mfccp->mf6cc_origin.sin6_addr), ip6_sprintf(&mfccp->mf6cc_mcastgrp.sin6_addr), mfccp->mf6cc_parent); #endif s = splnet(); rt->mf6c_parent = mfccp->mf6cc_parent; rt->mf6c_ifset = mfccp->mf6cc_ifset; splx(s); return 0; } /* * Find the entry for which the upcall was made and update */ s = splnet(); hash = MF6CHASH(mfccp->mf6cc_origin.sin6_addr, mfccp->mf6cc_mcastgrp.sin6_addr); for (rt = mf6ctable[hash], nstl = 0; rt; rt = rt->mf6c_next) { if (IN6_ARE_ADDR_EQUAL(&rt->mf6c_origin.sin6_addr, &mfccp->mf6cc_origin.sin6_addr) && IN6_ARE_ADDR_EQUAL(&rt->mf6c_mcastgrp.sin6_addr, &mfccp->mf6cc_mcastgrp.sin6_addr) && (rt->mf6c_stall != NULL)) { if (nstl++) log(LOG_ERR, "add_m6fc: %s o %s g %s p %x dbx %p\n", "multiple kernel entries", ip6_sprintf(&mfccp->mf6cc_origin.sin6_addr), ip6_sprintf(&mfccp->mf6cc_mcastgrp.sin6_addr), mfccp->mf6cc_parent, rt->mf6c_stall); #ifdef MRT6DEBUG if (mrt6debug & DEBUG_MFC) log(LOG_DEBUG, "add_m6fc o %s g %s p %x dbg %x\n", ip6_sprintf(&mfccp->mf6cc_origin.sin6_addr), ip6_sprintf(&mfccp->mf6cc_mcastgrp.sin6_addr), mfccp->mf6cc_parent, rt->mf6c_stall); #endif rt->mf6c_origin = mfccp->mf6cc_origin; rt->mf6c_mcastgrp = mfccp->mf6cc_mcastgrp; rt->mf6c_parent = mfccp->mf6cc_parent; rt->mf6c_ifset = mfccp->mf6cc_ifset; /* initialize pkt counters per src-grp */ rt->mf6c_pkt_cnt = 0; rt->mf6c_byte_cnt = 0; rt->mf6c_wrong_if = 0; rt->mf6c_expire = 0; /* Don't clean this guy up */ nexpire[hash]--; /* free packets Qed at the end of this entry */ for (rte = rt->mf6c_stall; rte != NULL; ) { struct rtdetq *n = rte->next; ip6_mdq(rte->m, rte->ifp, rt); m_freem(rte->m); #ifdef UPCALL_TIMING collate(&(rte->t)); #endif /* UPCALL_TIMING */ free(rte, M_MRTABLE); rte = n; } rt->mf6c_stall = NULL; } } /* * It is possible that an entry is being inserted without an upcall */ if (nstl == 0) { #ifdef MRT6DEBUG if (mrt6debug & DEBUG_MFC) log(LOG_DEBUG,"add_mfc no upcall h %d o %s g %s p %x\n", hash, ip6_sprintf(&mfccp->mf6cc_origin.sin6_addr), ip6_sprintf(&mfccp->mf6cc_mcastgrp.sin6_addr), mfccp->mf6cc_parent); #endif for (rt = mf6ctable[hash]; rt; rt = rt->mf6c_next) { - + if (IN6_ARE_ADDR_EQUAL(&rt->mf6c_origin.sin6_addr, &mfccp->mf6cc_origin.sin6_addr)&& IN6_ARE_ADDR_EQUAL(&rt->mf6c_mcastgrp.sin6_addr, &mfccp->mf6cc_mcastgrp.sin6_addr)) { rt->mf6c_origin = mfccp->mf6cc_origin; rt->mf6c_mcastgrp = mfccp->mf6cc_mcastgrp; rt->mf6c_parent = mfccp->mf6cc_parent; /* initialize pkt counters per src-grp */ rt->mf6c_pkt_cnt = 0; rt->mf6c_byte_cnt = 0; rt->mf6c_wrong_if = 0; if (rt->mf6c_expire) nexpire[hash]--; rt->mf6c_expire = 0; } } if (rt == NULL) { /* no upcall, so make a new entry */ rt = (struct mf6c *)malloc(sizeof(*rt), M_MRTABLE, M_NOWAIT); if (rt == NULL) { splx(s); return ENOBUFS; } - + /* insert new entry at head of hash chain */ rt->mf6c_origin = mfccp->mf6cc_origin; rt->mf6c_mcastgrp = mfccp->mf6cc_mcastgrp; rt->mf6c_parent = mfccp->mf6cc_parent; /* initialize pkt counters per src-grp */ rt->mf6c_pkt_cnt = 0; rt->mf6c_byte_cnt = 0; rt->mf6c_wrong_if = 0; rt->mf6c_expire = 0; rt->mf6c_stall = NULL; - + /* link into table */ rt->mf6c_next = mf6ctable[hash]; mf6ctable[hash] = rt; } } splx(s); return 0; } #ifdef UPCALL_TIMING /* * collect delay statistics on the upcalls */ static void collate(t) register struct timeval *t; { register u_long d; register struct timeval tp; register u_long delta; GET_TIME(tp); if (TV_LT(*t, tp)) { TV_DELTA(tp, *t, delta); - + d = delta >> 10; if (d > UPCALL_MAX) d = UPCALL_MAX; - + ++upcall_data[d]; } } #endif /* UPCALL_TIMING */ /* * Delete an mfc entry */ static int del_m6fc(mfccp) struct mf6cctl *mfccp; { struct sockaddr_in6 origin; struct sockaddr_in6 mcastgrp; struct mf6c *rt; struct mf6c **nptr; u_long hash; int s; origin = mfccp->mf6cc_origin; mcastgrp = mfccp->mf6cc_mcastgrp; hash = MF6CHASH(origin.sin6_addr, mcastgrp.sin6_addr); #ifdef MRT6DEBUG if (mrt6debug & DEBUG_MFC) log(LOG_DEBUG,"del_m6fc orig %s mcastgrp %s\n", ip6_sprintf(&origin.sin6_addr), ip6_sprintf(&mcastgrp.sin6_addr)); #endif s = splnet(); nptr = &mf6ctable[hash]; while ((rt = *nptr) != NULL) { if (IN6_ARE_ADDR_EQUAL(&origin.sin6_addr, &rt->mf6c_origin.sin6_addr) && IN6_ARE_ADDR_EQUAL(&mcastgrp.sin6_addr, &rt->mf6c_mcastgrp.sin6_addr) && rt->mf6c_stall == NULL) break; nptr = &rt->mf6c_next; } if (rt == NULL) { splx(s); return EADDRNOTAVAIL; } *nptr = rt->mf6c_next; free(rt, M_MRTABLE); splx(s); return 0; } static int socket_send(s, mm, src) struct socket *s; struct mbuf *mm; struct sockaddr_in6 *src; { if (s) { if (sbappendaddr(&s->so_rcv, (struct sockaddr *)src, mm, (struct mbuf *)0) != 0) { sorwakeup(s); return 0; } } m_freem(mm); return -1; } /* * IPv6 multicast forwarding function. This function assumes that the packet * pointed to by "ip6" has arrived on (or is about to be sent to) the interface * pointed to by "ifp", and the packet is to be relayed to other networks * that have members of the packet's destination IPv6 multicast group. * * The packet is returned unscathed to the caller, unless it is * erroneous, in which case a non-zero return value tells the caller to * discard it. */ int ip6_mforward(ip6, ifp, m) register struct ip6_hdr *ip6; struct ifnet *ifp; struct mbuf *m; { register struct mf6c *rt; register struct mif6 *mifp; register struct mbuf *mm; int s; mifi_t mifi; #ifdef MRT6DEBUG if (mrt6debug & DEBUG_FORWARD) log(LOG_DEBUG, "ip6_mforward: src %s, dst %s, ifindex %d\n", ip6_sprintf(&ip6->ip6_src), ip6_sprintf(&ip6->ip6_dst), ifp->if_index); #endif /* * Don't forward a packet with Hop limit of zero or one, * or a packet destined to a local-only group. */ if (ip6->ip6_hlim <= 1 || IN6_IS_ADDR_MC_NODELOCAL(&ip6->ip6_dst) || IN6_IS_ADDR_MC_LINKLOCAL(&ip6->ip6_dst)) return 0; ip6->ip6_hlim--; /* * Determine forwarding mifs from the forwarding cache table */ s = splnet(); MF6CFIND(ip6->ip6_src, ip6->ip6_dst, rt); /* Entry exists, so forward if necessary */ if (rt) { splx(s); return (ip6_mdq(m, ifp, rt)); } else { /* * If we don't have a route for packet's origin, * Make a copy of the packet & * send message to routing daemon */ register struct mbuf *mb0; register struct rtdetq *rte; register u_long hash; /* register int i, npkts;*/ #ifdef UPCALL_TIMING struct timeval tp; GET_TIME(tp); #endif /* UPCALL_TIMING */ mrt6stat.mrt6s_no_route++; #ifdef MRT6DEBUG if (mrt6debug & (DEBUG_FORWARD | DEBUG_MFC)) log(LOG_DEBUG, "ip6_mforward: no rte s %s g %s\n", ip6_sprintf(&ip6->ip6_src), ip6_sprintf(&ip6->ip6_dst)); #endif /* * Allocate mbufs early so that we don't do extra work if we * are just going to fail anyway. */ rte = (struct rtdetq *)malloc(sizeof(*rte), M_MRTABLE, M_NOWAIT); if (rte == NULL) { splx(s); return ENOBUFS; } mb0 = m_copy(m, 0, M_COPYALL); /* * Pullup packet header if needed before storing it, * as other references may modify it in the meantime. */ if (mb0 && (M_HASCL(mb0) || mb0->m_len < sizeof(struct ip6_hdr))) mb0 = m_pullup(mb0, sizeof(struct ip6_hdr)); if (mb0 == NULL) { free(rte, M_MRTABLE); splx(s); return ENOBUFS; } - + /* is there an upcall waiting for this packet? */ hash = MF6CHASH(ip6->ip6_src, ip6->ip6_dst); for (rt = mf6ctable[hash]; rt; rt = rt->mf6c_next) { if (IN6_ARE_ADDR_EQUAL(&ip6->ip6_src, &rt->mf6c_origin.sin6_addr) && IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &rt->mf6c_mcastgrp.sin6_addr) && (rt->mf6c_stall != NULL)) break; } if (rt == NULL) { struct mrt6msg *im; +#ifdef MRT6_OINIT + struct omrt6msg *oim; +#endif /* no upcall, so make a new entry */ rt = (struct mf6c *)malloc(sizeof(*rt), M_MRTABLE, M_NOWAIT); if (rt == NULL) { free(rte, M_MRTABLE); m_freem(mb0); splx(s); return ENOBUFS; } /* * Make a copy of the header to send to the user * level process */ mm = m_copy(mb0, 0, sizeof(struct ip6_hdr)); if (mm == NULL) { free(rte, M_MRTABLE); m_freem(mb0); free(rt, M_MRTABLE); splx(s); return ENOBUFS; } /* * Send message to routing daemon */ sin6.sin6_addr = ip6->ip6_src; + + im = NULL; +#ifdef MRT6_OINIT + oim = NULL; +#endif + switch (ip6_mrouter_ver) { +#ifdef MRT6_OINIT + case MRT6_OINIT: + oim = mtod(mm, struct omrt6msg *); + oim->im6_msgtype = MRT6MSG_NOCACHE; + oim->im6_mbz = 0; + break; +#endif + case MRT6_INIT: + im = mtod(mm, struct mrt6msg *); + im->im6_msgtype = MRT6MSG_NOCACHE; + im->im6_mbz = 0; + break; + default: + free(rte, M_MRTABLE); + m_freem(mb0); + free(rt, M_MRTABLE); + splx(s); + return EINVAL; + } - im = mtod(mm, struct mrt6msg *); - im->im6_msgtype = MRT6MSG_NOCACHE; - im->im6_mbz = 0; - #ifdef MRT6DEBUG if (mrt6debug & DEBUG_FORWARD) log(LOG_DEBUG, "getting the iif info in the kernel\n"); #endif for (mifp = mif6table, mifi = 0; mifi < nummifs && mifp->m6_ifp != ifp; mifp++, mifi++) ; - im->im6_mif = mifi; + switch (ip6_mrouter_ver) { +#ifdef MRT6_OINIT + case MRT6_OINIT: + oim->im6_mif = mifi; + break; +#endif + case MRT6_INIT: + im->im6_mif = mifi; + break; + } if (socket_send(ip6_mrouter, mm, &sin6) < 0) { log(LOG_WARNING, "ip6_mforward: ip6_mrouter " "socket queue full\n"); mrt6stat.mrt6s_upq_sockfull++; free(rte, M_MRTABLE); m_freem(mb0); free(rt, M_MRTABLE); splx(s); return ENOBUFS; } mrt6stat.mrt6s_upcalls++; /* insert new entry at head of hash chain */ bzero(rt, sizeof(*rt)); rt->mf6c_origin.sin6_family = AF_INET6; rt->mf6c_origin.sin6_len = sizeof(struct sockaddr_in6); rt->mf6c_origin.sin6_addr = ip6->ip6_src; rt->mf6c_mcastgrp.sin6_family = AF_INET6; rt->mf6c_mcastgrp.sin6_len = sizeof(struct sockaddr_in6); rt->mf6c_mcastgrp.sin6_addr = ip6->ip6_dst; rt->mf6c_expire = UPCALL_EXPIRE; nexpire[hash]++; rt->mf6c_parent = MF6C_INCOMPLETE_PARENT; /* link into table */ rt->mf6c_next = mf6ctable[hash]; mf6ctable[hash] = rt; /* Add this entry to the end of the queue */ rt->mf6c_stall = rte; } else { /* determine if q has overflowed */ struct rtdetq **p; register int npkts = 0; for (p = &rt->mf6c_stall; *p != NULL; p = &(*p)->next) if (++npkts > MAX_UPQ6) { mrt6stat.mrt6s_upq_ovflw++; free(rte, M_MRTABLE); m_freem(mb0); splx(s); return 0; } /* Add this entry to the end of the queue */ *p = rte; } rte->next = NULL; rte->m = mb0; rte->ifp = ifp; #ifdef UPCALL_TIMING rte->t = tp; #endif /* UPCALL_TIMING */ splx(s); return 0; } } /* * Clean up cache entries if upcalls are not serviced * Call from the Slow Timeout mechanism, every half second. */ static void expire_upcalls(unused) void *unused; { struct rtdetq *rte; struct mf6c *mfc, **nptr; int i; int s; s = splnet(); for (i = 0; i < MF6CTBLSIZ; i++) { if (nexpire[i] == 0) continue; nptr = &mf6ctable[i]; while ((mfc = *nptr) != NULL) { rte = mfc->mf6c_stall; /* * Skip real cache entries * Make sure it wasn't marked to not expire (shouldn't happen) * If it expires now */ if (rte != NULL && mfc->mf6c_expire != 0 && --mfc->mf6c_expire == 0) { #ifdef MRT6DEBUG if (mrt6debug & DEBUG_EXPIRE) log(LOG_DEBUG, "expire_upcalls: expiring (%s %s)\n", ip6_sprintf(&mfc->mf6c_origin.sin6_addr), ip6_sprintf(&mfc->mf6c_mcastgrp.sin6_addr)); #endif /* * drop all the packets * free the mbuf with the pkt, if, timing info */ do { struct rtdetq *n = rte->next; m_freem(rte->m); free(rte, M_MRTABLE); rte = n; } while (rte != NULL); mrt6stat.mrt6s_cache_cleanups++; nexpire[i]--; *nptr = mfc->mf6c_next; free(mfc, M_MRTABLE); } else { nptr = &mfc->mf6c_next; } } } splx(s); expire_upcalls_ch = - timeout(expire_upcalls, (caddr_t)NULL, EXPIRE_TIMEOUT); + timeout(expire_upcalls, (caddr_t)NULL, EXPIRE_TIMEOUT); } /* * Packet forwarding routine once entry in the cache is made */ static int ip6_mdq(m, ifp, rt) register struct mbuf *m; register struct ifnet *ifp; register struct mf6c *rt; { register struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); register mifi_t mifi, iif; register struct mif6 *mifp; register int plen = m->m_pkthdr.len; /* * Macro to send packet on mif. Since RSVP packets don't get counted on * input, they shouldn't get counted on output, so statistics keeping is * seperate. */ -#define MC6_SEND(ip6,mifp,m) { \ - if ((mifp)->m6_flags & MIFF_REGISTER) \ - register_send((ip6), (mifp), (m)); \ - else \ - phyint_send((ip6), (mifp), (m)); \ -} +#define MC6_SEND(ip6, mifp, m) do { \ + if ((mifp)->m6_flags & MIFF_REGISTER) \ + register_send((ip6), (mifp), (m)); \ + else \ + phyint_send((ip6), (mifp), (m)); \ +} while (0) /* * Don't forward if it didn't arrive from the parent mif * for its origin. */ mifi = rt->mf6c_parent; if ((mifi >= nummifs) || (mif6table[mifi].m6_ifp != ifp)) { /* came in the wrong interface */ #ifdef MRT6DEBUG if (mrt6debug & DEBUG_FORWARD) log(LOG_DEBUG, "wrong if: ifid %d mifi %d mififid %x\n", ifp->if_index, mifi, mif6table[mifi].m6_ifp->if_index); #endif mrt6stat.mrt6s_wrong_if++; rt->mf6c_wrong_if++; /* * If we are doing PIM processing, and we are forwarding * packets on this interface, send a message to the * routing daemon. */ - if(mifi < nummifs) /* have to make sure this is a valid mif */ - if(mif6table[mifi].m6_ifp) + /* have to make sure this is a valid mif */ + if (mifi < nummifs && mif6table[mifi].m6_ifp) + if (pim6 && (m->m_flags & M_LOOP) == 0) { + /* + * Check the M_LOOP flag to avoid an + * unnecessary PIM assert. + * XXX: M_LOOP is an ad-hoc hack... + */ + static struct sockaddr_in6 sin6 = + { sizeof(sin6), AF_INET6 }; - if (pim6 && (m->m_flags & M_LOOP) == 0) { - /* - * Check the M_LOOP flag to avoid an - * unnecessary PIM assert. - * XXX: M_LOOP is an ad-hoc hack... - */ - static struct sockaddr_in6 sin6 = - { sizeof(sin6), AF_INET6 }; + register struct mbuf *mm; + struct mrt6msg *im; +#ifdef MRT6_OINIT + struct omrt6msg *oim; +#endif - register struct mbuf *mm; - struct mrt6msg *im; - - mm = m_copy(m, 0, - sizeof(struct ip6_hdr)); - if (mm && - (M_HASCL(mm) || - mm->m_len < sizeof(struct ip6_hdr))) - mm = m_pullup(mm, sizeof(struct ip6_hdr)); - if (mm == NULL) - return ENOBUFS; - + mm = m_copy(m, 0, sizeof(struct ip6_hdr)); + if (mm && + (M_HASCL(mm) || + mm->m_len < sizeof(struct ip6_hdr))) + mm = m_pullup(mm, sizeof(struct ip6_hdr)); + if (mm == NULL) + return ENOBUFS; + +#ifdef MRT6_OINIT + oim = NULL; +#endif + im = NULL; + switch (ip6_mrouter_ver) { +#ifdef MRT6_OINIT + case MRT6_OINIT: + oim = mtod(mm, struct omrt6msg *); + oim->im6_msgtype = MRT6MSG_WRONGMIF; + oim->im6_mbz = 0; + break; +#endif + case MRT6_INIT: im = mtod(mm, struct mrt6msg *); - im->im6_msgtype = MRT6MSG_WRONGMIF; - im->im6_mbz = 0; + im->im6_msgtype = MRT6MSG_WRONGMIF; + break; + default: + m_freem(mm); + return EINVAL; + } - for (mifp = mif6table, iif = 0; - iif < nummifs && mifp && - mifp->m6_ifp != ifp; - mifp++, iif++); + for (mifp = mif6table, iif = 0; + iif < nummifs && mifp && + mifp->m6_ifp != ifp; + mifp++, iif++) + ; - im->im6_mif = iif; - + switch (ip6_mrouter_ver) { +#ifdef MRT6_OINIT + case MRT6_OINIT: + oim->im6_mif = iif; + sin6.sin6_addr = oim->im6_src; + break; +#endif + case MRT6_INIT: + im->im6_mif = iif; sin6.sin6_addr = im->im6_src; + break; + } - mrt6stat.mrt6s_upcalls++; + mrt6stat.mrt6s_upcalls++; - if (socket_send(ip6_mrouter, mm, - &sin6) < 0) { + if (socket_send(ip6_mrouter, mm, &sin6) < 0) { #ifdef MRT6DEBUG - if (mrt6debug) - log(LOG_WARNING, "mdq, ip6_mrouter socket queue full\n"); + if (mrt6debug) + log(LOG_WARNING, "mdq, ip6_mrouter socket queue full\n"); #endif - ++mrt6stat.mrt6s_upq_sockfull; - return ENOBUFS; - } /* if socket Q full */ - } /* if PIM */ + ++mrt6stat.mrt6s_upq_sockfull; + return ENOBUFS; + } /* if socket Q full */ + } /* if PIM */ return 0; } /* if wrong iif */ /* If I sourced this packet, it counts as output, else it was input. */ if (m->m_pkthdr.rcvif == NULL) { /* XXX: is rcvif really NULL when output?? */ mif6table[mifi].m6_pkt_out++; mif6table[mifi].m6_bytes_out += plen; } else { mif6table[mifi].m6_pkt_in++; mif6table[mifi].m6_bytes_in += plen; } rt->mf6c_pkt_cnt++; rt->mf6c_byte_cnt += plen; /* * For each mif, forward a copy of the packet if there are group * members downstream on the interface. */ for (mifp = mif6table, mifi = 0; mifi < nummifs; mifp++, mifi++) if (IF_ISSET(mifi, &rt->mf6c_ifset)) { + /* + * check if the outgoing packet is going to break + * a scope boundary. + * XXX For packets through PIM register tunnel + * interface, we believe a routing daemon. + */ + if ((mif6table[rt->mf6c_parent].m6_flags & + MIFF_REGISTER) == 0 && + (mif6table[mifi].m6_flags & MIFF_REGISTER) == 0 && + (in6_addr2scopeid(ifp, &ip6->ip6_dst) != + in6_addr2scopeid(mif6table[mifi].m6_ifp, + &ip6->ip6_dst) || + in6_addr2scopeid(ifp, &ip6->ip6_src) != + in6_addr2scopeid(mif6table[mifi].m6_ifp, + &ip6->ip6_src))) { + ip6stat.ip6s_badscope++; + continue; + } + mifp->m6_pkt_out++; mifp->m6_bytes_out += plen; MC6_SEND(ip6, mifp, m); } return 0; } static void phyint_send(ip6, mifp, m) struct ip6_hdr *ip6; struct mif6 *mifp; struct mbuf *m; { register struct mbuf *mb_copy; struct ifnet *ifp = mifp->m6_ifp; int error = 0; int s = splnet(); static struct route_in6 ro6; struct in6_multi *in6m; /* * Make a new reference to the packet; make sure that * the IPv6 header is actually copied, not just referenced, * so that ip6_output() only scribbles on the copy. */ mb_copy = m_copy(m, 0, M_COPYALL); if (mb_copy && (M_HASCL(mb_copy) || mb_copy->m_len < sizeof(struct ip6_hdr))) mb_copy = m_pullup(mb_copy, sizeof(struct ip6_hdr)); if (mb_copy == NULL) return; /* set MCAST flag to the outgoing packet */ mb_copy->m_flags |= M_MCAST; /* * If we sourced the packet, call ip6_output since we may devide * the packet into fragments when the packet is too big for the * outgoing interface. * Otherwise, we can simply send the packet to the interface * sending queue. */ if (m->m_pkthdr.rcvif == NULL) { struct ip6_moptions im6o; im6o.im6o_multicast_ifp = ifp; /* XXX: ip6_output will override ip6->ip6_hlim */ im6o.im6o_multicast_hlim = ip6->ip6_hlim; im6o.im6o_multicast_loop = 1; error = ip6_output(mb_copy, NULL, &ro6, IPV6_FORWARDING, &im6o, NULL); #ifdef MRT6DEBUG if (mrt6debug & DEBUG_XMIT) log(LOG_DEBUG, "phyint_send on mif %d err %d\n", mifp - mif6table, error); #endif splx(s); return; } /* * If we belong to the destination multicast group * on the outgoing interface, loop back a copy. */ IN6_LOOKUP_MULTI(ip6->ip6_dst, ifp, in6m); if (in6m != NULL) { ro6.ro_dst.sin6_len = sizeof(struct sockaddr_in6); ro6.ro_dst.sin6_family = AF_INET6; ro6.ro_dst.sin6_addr = ip6->ip6_dst; ip6_mloopback(ifp, m, &ro6.ro_dst); } /* * Put the packet into the sending queue of the outgoing interface * if it would fit in the MTU of the interface. */ if (mb_copy->m_pkthdr.len < ifp->if_mtu || ifp->if_mtu < IPV6_MMTU) { ro6.ro_dst.sin6_len = sizeof(struct sockaddr_in6); ro6.ro_dst.sin6_family = AF_INET6; ro6.ro_dst.sin6_addr = ip6->ip6_dst; /* * We just call if_output instead of nd6_output here, since * we need no ND for a multicast forwarded packet...right? */ error = (*ifp->if_output)(ifp, mb_copy, (struct sockaddr *)&ro6.ro_dst, NULL); #ifdef MRT6DEBUG if (mrt6debug & DEBUG_XMIT) log(LOG_DEBUG, "phyint_send on mif %d err %d\n", mifp - mif6table, error); #endif } else { #ifdef MULTICAST_PMTUD icmp6_error(mb_copy, ICMP6_PACKET_TOO_BIG, 0, ifp->if_mtu); return; #else #ifdef MRT6DEBUG if (mrt6debug & DEBUG_XMIT) log(LOG_DEBUG, "phyint_send: packet too big on %s%u o %s g %s" " size %d(discarded)\n", ifp->if_name, ifp->if_unit, ip6_sprintf(&ip6->ip6_src), ip6_sprintf(&ip6->ip6_dst), mb_copy->m_pkthdr.len); #endif /* MRT6DEBUG */ m_freem(mb_copy); /* simply discard the packet */ return; #endif } } static int register_send(ip6, mif, m) register struct ip6_hdr *ip6; struct mif6 *mif; register struct mbuf *m; { register struct mbuf *mm; register int i, len = m->m_pkthdr.len; static struct sockaddr_in6 sin6 = { sizeof(sin6), AF_INET6 }; struct mrt6msg *im6; #ifdef MRT6DEBUG if (mrt6debug) log(LOG_DEBUG, "** IPv6 register_send **\n src %s dst %s\n", ip6_sprintf(&ip6->ip6_src), ip6_sprintf(&ip6->ip6_dst)); #endif ++pim6stat.pim6s_snd_registers; /* Make a copy of the packet to send to the user level process */ MGETHDR(mm, M_DONTWAIT, MT_HEADER); if (mm == NULL) return ENOBUFS; mm->m_data += max_linkhdr; mm->m_len = sizeof(struct ip6_hdr); if ((mm->m_next = m_copy(m, 0, M_COPYALL)) == NULL) { m_freem(mm); return ENOBUFS; } i = MHLEN - M_LEADINGSPACE(mm); if (i > len) i = len; mm = m_pullup(mm, i); if (mm == NULL){ m_freem(mm); return ENOBUFS; } /* TODO: check it! */ mm->m_pkthdr.len = len + sizeof(struct ip6_hdr); /* * Send message to routing daemon */ sin6.sin6_addr = ip6->ip6_src; im6 = mtod(mm, struct mrt6msg *); im6->im6_msgtype = MRT6MSG_WHOLEPKT; im6->im6_mbz = 0; im6->im6_mif = mif - mif6table; /* iif info is not given for reg. encap.n */ mrt6stat.mrt6s_upcalls++; if (socket_send(ip6_mrouter, mm, &sin6) < 0) { #ifdef MRT6DEBUG if (mrt6debug) log(LOG_WARNING, "register_send: ip_mrouter socket queue full\n"); #endif ++mrt6stat.mrt6s_upq_sockfull; return ENOBUFS; } return 0; } /* * PIM sparse mode hook * Receives the pim control messages, and passes them up to the listening * socket, using rip6_input. * The only message processed is the REGISTER pim message; the pim header * is stripped off, and the inner packet is passed to register_mforward. */ int pim6_input(mp, offp, proto) struct mbuf **mp; int *offp, proto; { register struct pim *pim; /* pointer to a pim struct */ register struct ip6_hdr *ip6; register int pimlen; struct mbuf *m = *mp; int minlen; int off = *offp; ++pim6stat.pim6s_rcv_total; ip6 = mtod(m, struct ip6_hdr *); pimlen = m->m_pkthdr.len - *offp; /* * Validate lengths */ if (pimlen < PIM_MINLEN) { ++pim6stat.pim6s_rcv_tooshort; #ifdef MRT6DEBUG if (mrt6debug & DEBUG_PIM) log(LOG_DEBUG,"pim6_input: PIM packet too short\n"); #endif m_freem(m); return(IPPROTO_DONE); } /* * if the packet is at least as big as a REGISTER, go ahead * and grab the PIM REGISTER header size, to avoid another * possible m_pullup() later. * * PIM_MINLEN == pimhdr + u_int32 == 8 * PIM6_REG_MINLEN == pimhdr + reghdr + eip6hdr == 4 + 4 + 40 */ minlen = (pimlen >= PIM6_REG_MINLEN) ? PIM6_REG_MINLEN : PIM_MINLEN; /* * Make sure that the IP6 and PIM headers in contiguous memory, and * possibly the PIM REGISTER header */ +#ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, off, minlen, IPPROTO_DONE); /* adjust pointer */ ip6 = mtod(m, struct ip6_hdr *); /* adjust mbuf to point to the PIM header */ pim = (struct pim *)((caddr_t)ip6 + off); +#else + IP6_EXTHDR_GET(pim, struct pim *, m, off, minlen); + if (pim == NULL) { + pim6stat.pim6s_rcv_tooshort++; + return IPPROTO_DONE; + } +#endif -#define PIM6_CHECKSUM +#define PIM6_CHECKSUM #ifdef PIM6_CHECKSUM { int cksumlen; /* * Validate checksum. * If PIM REGISTER, exclude the data packet */ if (pim->pim_type == PIM_REGISTER) cksumlen = PIM_MINLEN; else cksumlen = pimlen; if (in6_cksum(m, IPPROTO_PIM, off, cksumlen)) { ++pim6stat.pim6s_rcv_badsum; #ifdef MRT6DEBUG if (mrt6debug & DEBUG_PIM) log(LOG_DEBUG, "pim6_input: invalid checksum\n"); #endif m_freem(m); return(IPPROTO_DONE); } } #endif /* PIM_CHECKSUM */ /* PIM version check */ if (pim->pim_ver != PIM_VERSION) { ++pim6stat.pim6s_rcv_badversion; #ifdef MRT6DEBUG log(LOG_ERR, "pim6_input: incorrect version %d, expecting %d\n", pim->pim_ver, PIM_VERSION); #endif m_freem(m); return(IPPROTO_DONE); } if (pim->pim_type == PIM_REGISTER) { /* * since this is a REGISTER, we'll make a copy of the register * headers ip6+pim+u_int32_t+encap_ip6, to be passed up to the * routing daemon. */ static struct sockaddr_in6 dst = { sizeof(dst), AF_INET6 }; struct mbuf *mcp; struct ip6_hdr *eip6; u_int32_t *reghdr; int rc; - + ++pim6stat.pim6s_rcv_registers; if ((reg_mif_num >= nummifs) || (reg_mif_num == (mifi_t) -1)) { #ifdef MRT6DEBUG if (mrt6debug & DEBUG_PIM) log(LOG_DEBUG, "pim6_input: register mif not set: %d\n", reg_mif_num); #endif m_freem(m); return(IPPROTO_DONE); } - + reghdr = (u_int32_t *)(pim + 1); - + if ((ntohl(*reghdr) & PIM_NULL_REGISTER)) goto pim6_input_to_daemon; /* * Validate length */ if (pimlen < PIM6_REG_MINLEN) { ++pim6stat.pim6s_rcv_tooshort; ++pim6stat.pim6s_rcv_badregisters; #ifdef MRT6DEBUG log(LOG_ERR, "pim6_input: register packet size too " "small %d from %s\n", pimlen, ip6_sprintf(&ip6->ip6_src)); #endif m_freem(m); return(IPPROTO_DONE); } - + eip6 = (struct ip6_hdr *) (reghdr + 1); -#ifdef MRT6DEBUG +#ifdef MRT6DEBUG if (mrt6debug & DEBUG_PIM) log(LOG_DEBUG, "pim6_input[register], eip6: %s -> %s, " "eip6 plen %d\n", ip6_sprintf(&eip6->ip6_src), ip6_sprintf(&eip6->ip6_dst), ntohs(eip6->ip6_plen)); #endif - + /* verify the inner packet is destined to a mcast group */ if (!IN6_IS_ADDR_MULTICAST(&eip6->ip6_dst)) { ++pim6stat.pim6s_rcv_badregisters; #ifdef MRT6DEBUG if (mrt6debug & DEBUG_PIM) log(LOG_DEBUG, "pim6_input: inner packet of register " "is not multicast %s\n", ip6_sprintf(&eip6->ip6_dst)); #endif m_freem(m); return(IPPROTO_DONE); } - + /* * make a copy of the whole header to pass to the daemon later. */ mcp = m_copy(m, 0, off + PIM6_REG_MINLEN); if (mcp == NULL) { #ifdef MRT6DEBUG log(LOG_ERR, "pim6_input: pim register: " "could not copy register head\n"); #endif m_freem(m); return(IPPROTO_DONE); } - + /* * forward the inner ip6 packet; point m_data at the inner ip6. */ m_adj(m, off + PIM_MINLEN); #ifdef MRT6DEBUG if (mrt6debug & DEBUG_PIM) { log(LOG_DEBUG, "pim6_input: forwarding decapsulated register: " "src %s, dst %s, mif %d\n", ip6_sprintf(&eip6->ip6_src), ip6_sprintf(&eip6->ip6_dst), reg_mif_num); } #endif rc = if_simloop(mif6table[reg_mif_num].m6_ifp, m, - dst.sin6_family, NULL); - + dst.sin6_family, NULL); + /* prepare the register head to send to the mrouting daemon */ m = mcp; } /* * Pass the PIM message up to the daemon; if it is a register message * pass the 'head' only up to the daemon. This includes the * encapsulator ip6 header, pim header, register header and the * encapsulated ip6 header. */ pim6_input_to_daemon: rip6_input(&m, offp, proto); return(IPPROTO_DONE); } Index: head/sys/netinet6/ip6_mroute.h =================================================================== --- head/sys/netinet6/ip6_mroute.h (revision 62586) +++ head/sys/netinet6/ip6_mroute.h (revision 62587) @@ -1,484 +1,277 @@ +/* $FreeBSD$ */ +/* $KAME: ip6_mroute.h,v 1.10 2000/05/19 02:38:53 itojun Exp $ */ + /* * Copyright (C) 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* BSDI ip_mroute.h,v 2.5 1996/10/11 16:01:48 pjd Exp */ /* * Definitions for IP multicast forwarding. * * Written by David Waitzman, BBN Labs, August 1988. * Modified by Steve Deering, Stanford, February 1989. * Modified by Ajit Thyagarajan, PARC, August 1993. * Modified by Ajit Thyagarajan, PARC, August 1994. * Modified by Ahmed Helmy, USC, September 1996. * * MROUTING Revision: 1.2 */ #ifndef _NETINET6_IP6_MROUTE_H_ -#define _NETINET6_IP6_MROUTE_H_ - -/* - * Multicast Routing set/getsockopt commands. - */ -#define MRT6_INIT 100 /* initialize forwarder */ -#define MRT6_DONE 101 /* shut down forwarder */ -#define MRT6_ADD_MIF 102 /* add multicast interface */ -#define MRT6_DEL_MIF 103 /* delete multicast interface */ -#define MRT6_ADD_MFC 104 /* insert forwarding cache entry */ -#define MRT6_DEL_MFC 105 /* delete forwarding cache entry */ -#define MRT6_PIM 107 /* enable pim code */ - -#define GET_TIME(t) microtime(&t) - -/* - * Types and macros for handling bitmaps with one bit per multicast interface. - */ -typedef u_short mifi_t; /* type of a mif index */ -#define MAXMIFS 64 - -#ifndef IF_SETSIZE -#define IF_SETSIZE 256 -#endif - -typedef long if_mask; -#define NIFBITS (sizeof(if_mask) * NBBY) /* bits per mask */ - -#ifndef howmany -#define howmany(x, y) (((x) + ((y) - 1)) / (y)) -#endif - -typedef struct if_set { - fd_mask ifs_bits[howmany(IF_SETSIZE, NIFBITS)]; -} if_set; - -#define IF_SET(n, p) ((p)->ifs_bits[(n)/NIFBITS] |= (1 << ((n) % NIFBITS))) -#define IF_CLR(n, p) ((p)->ifs_bits[(n)/NIFBITS] &= ~(1 << ((n) % NIFBITS))) -#define IF_ISSET(n, p) ((p)->ifs_bits[(n)/NIFBITS] & (1 << ((n) % NIFBITS))) -#define IF_COPY(f, t) bcopy(f, t, sizeof(*(f))) -#define IF_ZERO(p) bzero(p, sizeof(*(p))) - -/* - * Argument structure for MRT6_ADD_IF. - */ -struct mif6ctl { - mifi_t mif6c_mifi; /* the index of the mif to be added */ - u_char mif6c_flags; /* MIFF_ flags defined below */ - u_short mif6c_pifi; /* the index of the physical IF */ -}; - -#define MIFF_REGISTER 0x1 /* mif represents a register end-point */ - -/* - * Argument structure for MRT6_ADD_MFC and MRT6_DEL_MFC - */ -struct mf6cctl { - struct sockaddr_in6 mf6cc_origin; /* IPv6 origin of mcasts */ - struct sockaddr_in6 mf6cc_mcastgrp; /* multicast group associated */ - mifi_t mf6cc_parent; /* incoming ifindex */ - struct if_set mf6cc_ifset; /* set of forwarding ifs */ -}; - -/* - * The kernel's multicast routing statistics. - */ -struct mrt6stat { - u_quad_t mrt6s_mfc_lookups; /* # forw. cache hash table hits */ - u_quad_t mrt6s_mfc_misses; /* # forw. cache hash table misses */ - u_quad_t mrt6s_upcalls; /* # calls to mrouted */ - u_quad_t mrt6s_no_route; /* no route for packet's origin */ - u_quad_t mrt6s_bad_tunnel; /* malformed tunnel options */ - u_quad_t mrt6s_cant_tunnel; /* no room for tunnel options */ - u_quad_t mrt6s_wrong_if; /* arrived on wrong interface */ - u_quad_t mrt6s_upq_ovflw; /* upcall Q overflow */ - u_quad_t mrt6s_cache_cleanups; /* # entries with no upcalls */ - u_quad_t mrt6s_drop_sel; /* pkts dropped selectively */ - u_quad_t mrt6s_q_overflow; /* pkts dropped - Q overflow */ - u_quad_t mrt6s_pkt2large; /* pkts dropped - size > BKT SIZE */ - u_quad_t mrt6s_upq_sockfull; /* upcalls dropped - socket full */ -}; - -/* - * Struct used to communicate from kernel to multicast router - * note the convenient similarity to an IPv6 header. - */ -struct mrt6msg { - u_long unused1; - u_char im6_msgtype; /* what type of message */ -#define MRT6MSG_NOCACHE 1 -#define MRT6MSG_WRONGMIF 2 -#define MRT6MSG_WHOLEPKT 3 /* used for user level encap*/ - u_char im6_mbz; /* must be zero */ - u_char im6_mif; /* mif rec'd on */ - u_char unused2; - struct in6_addr im6_src, im6_dst; -}; - -/* - * Argument structure used by multicast routing daemon to get src-grp - * packet counts - */ -struct sioc_sg_req6 { - struct sockaddr_in6 src; - struct sockaddr_in6 grp; - u_quad_t pktcnt; - u_quad_t bytecnt; - u_quad_t wrong_if; -}; - -/* - * Argument structure used by mrouted to get mif pkt counts - */ -struct sioc_mif_req6 { - mifi_t mifi; /* mif number */ - u_quad_t icount; /* Input packet count on mif */ - u_quad_t ocount; /* Output packet count on mif */ - u_quad_t ibytes; /* Input byte count on mif */ - u_quad_t obytes; /* Output byte count on mif */ -}; - -#if defined(_KERNEL) || defined(KERNEL) -/* - * The kernel's multicast-interface structure. - */ -struct mif6 { - u_char m6_flags; /* MIFF_ flags defined above */ - u_int m6_rate_limit; /* max rate */ - struct in6_addr m6_lcl_addr; /* local interface address */ - struct ifnet *m6_ifp; /* pointer to interface */ - u_quad_t m6_pkt_in; /* # pkts in on interface */ - u_quad_t m6_pkt_out; /* # pkts out on interface */ - u_quad_t m6_bytes_in; /* # bytes in on interface */ - u_quad_t m6_bytes_out; /* # bytes out on interface */ - struct route_in6 m6_route;/* cached route if this is a tunnel */ -}; - -/* - * The kernel's multicast forwarding cache entry structure - */ -struct mf6c { - struct sockaddr_in6 mf6c_origin; /* IPv6 origin of mcasts */ - struct sockaddr_in6 mf6c_mcastgrp; /* multicast group associated*/ - mifi_t mf6c_parent; /* incoming IF */ - struct if_set mf6c_ifset; /* set of outgoing IFs */ - u_quad_t mf6c_pkt_cnt; /* pkt count for src-grp */ - u_quad_t mf6c_byte_cnt; /* byte count for src-grp */ - u_quad_t mf6c_wrong_if; /* wrong if for src-grp */ - int mf6c_expire; /* time to clean entry up */ - struct timeval mf6c_last_assert; /* last time I sent an assert*/ - struct rtdetq *mf6c_stall; /* pkts waiting for route */ - struct mf6c *mf6c_next; /* hash table linkage */ -}; - -#define MF6C_INCOMPLETE_PARENT ((mifi_t)-1) - -/* - * Argument structure used for pkt info. while upcall is made - */ -#ifndef _NETINET_IP_MROUTE_H_ -struct rtdetq { /* XXX: rtdetq is also defined in ip_mroute.h */ - struct mbuf *m; /* A copy of the packet */ - struct ifnet *ifp; /* Interface pkt came in on */ -#ifdef UPCALL_TIMING - struct timeval t; /* Timestamp */ -#endif /* UPCALL_TIMING */ - struct rtdetq *next; -}; -#endif /* _NETINET_IP_MROUTE_H_ */ - -#define MF6CTBLSIZ 256 -#if (MF6CTBLSIZ & (MF6CTBLSIZ - 1)) == 0 /* from sys:route.h */ -#define MF6CHASHMOD(h) ((h) & (MF6CTBLSIZ - 1)) -#else -#define MF6CHASHMOD(h) ((h) % MF6CTBLSIZ) -#endif - -#define MAX_UPQ6 4 /* max. no of pkts in upcall Q */ - -int ip6_mrouter_set __P((struct socket *so, struct sockopt *sopt)); -int ip6_mrouter_get __P((struct socket *so, struct sockopt *sopt)); -int ip6_mrouter_done __P((void)); -int mrt6_ioctl __P((int, caddr_t)); -#endif /* _KERNEL */ - -#endif /* !_NETINET6_IP6_MROUTE_H_ */ -/* - * Copyright (C) 1998 WIDE Project. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the project nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -/* BSDI ip_mroute.h,v 2.5 1996/10/11 16:01:48 pjd Exp */ - -/* - * Definitions for IP multicast forwarding. - * - * Written by David Waitzman, BBN Labs, August 1988. - * Modified by Steve Deering, Stanford, February 1989. - * Modified by Ajit Thyagarajan, PARC, August 1993. - * Modified by Ajit Thyagarajan, PARC, August 1994. - * Modified by Ahmed Helmy, USC, September 1996. - * - * MROUTING Revision: 1.2 - */ - -#ifndef _NETINET6_IP6_MROUTE_H_ #define _NETINET6_IP6_MROUTE_H_ /* * Multicast Routing set/getsockopt commands. */ -#define MRT6_INIT 100 /* initialize forwarder */ +#ifdef _KERNEL +#define MRT6_OINIT 100 /* initialize forwarder (omrt6msg) */ +#endif #define MRT6_DONE 101 /* shut down forwarder */ #define MRT6_ADD_MIF 102 /* add multicast interface */ #define MRT6_DEL_MIF 103 /* delete multicast interface */ #define MRT6_ADD_MFC 104 /* insert forwarding cache entry */ #define MRT6_DEL_MFC 105 /* delete forwarding cache entry */ #define MRT6_PIM 107 /* enable pim code */ +#define MRT6_INIT 108 /* initialize forwarder (mrt6msg) */ -#if BSD >= 199103 +#if BSD >= 199103 #define GET_TIME(t) microtime(&t) #elif defined(sun) #define GET_TIME(t) uniqtime(&t) #else #define GET_TIME(t) ((t) = time) #endif /* * Types and macros for handling bitmaps with one bit per multicast interface. */ typedef u_short mifi_t; /* type of a mif index */ #define MAXMIFS 64 #ifndef IF_SETSIZE #define IF_SETSIZE 256 #endif typedef long if_mask; #define NIFBITS (sizeof(if_mask) * NBBY) /* bits per mask */ #ifndef howmany #define howmany(x, y) (((x) + ((y) - 1)) / (y)) #endif typedef struct if_set { fd_mask ifs_bits[howmany(IF_SETSIZE, NIFBITS)]; } if_set; #define IF_SET(n, p) ((p)->ifs_bits[(n)/NIFBITS] |= (1 << ((n) % NIFBITS))) #define IF_CLR(n, p) ((p)->ifs_bits[(n)/NIFBITS] &= ~(1 << ((n) % NIFBITS))) #define IF_ISSET(n, p) ((p)->ifs_bits[(n)/NIFBITS] & (1 << ((n) % NIFBITS))) #define IF_COPY(f, t) bcopy(f, t, sizeof(*(f))) #define IF_ZERO(p) bzero(p, sizeof(*(p))) /* * Argument structure for MRT6_ADD_IF. */ struct mif6ctl { mifi_t mif6c_mifi; /* the index of the mif to be added */ u_char mif6c_flags; /* MIFF_ flags defined below */ u_short mif6c_pifi; /* the index of the physical IF */ #ifdef notyet u_int mif6c_rate_limit; /* max rate */ #endif }; #define MIFF_REGISTER 0x1 /* mif represents a register end-point */ /* * Argument structure for MRT6_ADD_MFC and MRT6_DEL_MFC */ struct mf6cctl { struct sockaddr_in6 mf6cc_origin; /* IPv6 origin of mcasts */ struct sockaddr_in6 mf6cc_mcastgrp; /* multicast group associated */ mifi_t mf6cc_parent; /* incoming ifindex */ struct if_set mf6cc_ifset; /* set of forwarding ifs */ }; /* * The kernel's multicast routing statistics. */ struct mrt6stat { u_quad_t mrt6s_mfc_lookups; /* # forw. cache hash table hits */ u_quad_t mrt6s_mfc_misses; /* # forw. cache hash table misses */ u_quad_t mrt6s_upcalls; /* # calls to mrouted */ u_quad_t mrt6s_no_route; /* no route for packet's origin */ u_quad_t mrt6s_bad_tunnel; /* malformed tunnel options */ u_quad_t mrt6s_cant_tunnel; /* no room for tunnel options */ u_quad_t mrt6s_wrong_if; /* arrived on wrong interface */ u_quad_t mrt6s_upq_ovflw; /* upcall Q overflow */ u_quad_t mrt6s_cache_cleanups; /* # entries with no upcalls */ u_quad_t mrt6s_drop_sel; /* pkts dropped selectively */ u_quad_t mrt6s_q_overflow; /* pkts dropped - Q overflow */ u_quad_t mrt6s_pkt2large; /* pkts dropped - size > BKT SIZE */ u_quad_t mrt6s_upq_sockfull; /* upcalls dropped - socket full */ }; +#ifdef MRT6_OINIT /* * Struct used to communicate from kernel to multicast router * note the convenient similarity to an IPv6 header. + * XXX old version, superseded by mrt6msg. */ -struct mrt6msg { +struct omrt6msg { u_long unused1; u_char im6_msgtype; /* what type of message */ -#define MRT6MSG_NOCACHE 1 +#if 0 +#define MRT6MSG_NOCACHE 1 #define MRT6MSG_WRONGMIF 2 #define MRT6MSG_WHOLEPKT 3 /* used for user level encap*/ +#endif u_char im6_mbz; /* must be zero */ u_char im6_mif; /* mif rec'd on */ u_char unused2; struct in6_addr im6_src, im6_dst; }; +#endif /* + * Structure used to communicate from kernel to multicast router. + * We'll overlay the structure onto an MLD header (not an IPv6 header + * like igmpmsg{} used for IPv4 implementation). This is because this + * structure will be passed via an IPv6 raw socket, on which an application + * will only receive the payload i.e. the data after the IPv6 header and all + * the extension headers. (see Section 3 of draft-ietf-ipngwg-2292bis-01) + */ +struct mrt6msg { +#define MRT6MSG_NOCACHE 1 +#define MRT6MSG_WRONGMIF 2 +#define MRT6MSG_WHOLEPKT 3 /* used for user level encap*/ + u_char im6_mbz; /* must be zero */ + u_char im6_msgtype; /* what type of message */ + u_int16_t im6_mif; /* mif rec'd on */ + u_int32_t im6_pad; /* padding for 64bit arch */ + struct in6_addr im6_src, im6_dst; +}; + +/* * Argument structure used by multicast routing daemon to get src-grp * packet counts */ struct sioc_sg_req6 { struct sockaddr_in6 src; struct sockaddr_in6 grp; u_quad_t pktcnt; u_quad_t bytecnt; u_quad_t wrong_if; }; /* * Argument structure used by mrouted to get mif pkt counts */ struct sioc_mif_req6 { mifi_t mifi; /* mif number */ u_quad_t icount; /* Input packet count on mif */ u_quad_t ocount; /* Output packet count on mif */ u_quad_t ibytes; /* Input byte count on mif */ u_quad_t obytes; /* Output byte count on mif */ }; #if defined(_KERNEL) || defined(KERNEL) /* * The kernel's multicast-interface structure. */ struct mif6 { u_char m6_flags; /* MIFF_ flags defined above */ u_int m6_rate_limit; /* max rate */ #ifdef notyet struct tbf *m6_tbf; /* token bucket structure at intf. */ -#endif +#endif struct in6_addr m6_lcl_addr; /* local interface address */ struct ifnet *m6_ifp; /* pointer to interface */ u_quad_t m6_pkt_in; /* # pkts in on interface */ u_quad_t m6_pkt_out; /* # pkts out on interface */ u_quad_t m6_bytes_in; /* # bytes in on interface */ u_quad_t m6_bytes_out; /* # bytes out on interface */ struct route_in6 m6_route;/* cached route if this is a tunnel */ #ifdef notyet u_int m6_rsvp_on; /* RSVP listening on this vif */ struct socket *m6_rsvpd; /* RSVP daemon socket */ -#endif +#endif }; /* - * The kernel's multicast forwarding cache entry structure + * The kernel's multicast forwarding cache entry structure */ struct mf6c { struct sockaddr_in6 mf6c_origin; /* IPv6 origin of mcasts */ struct sockaddr_in6 mf6c_mcastgrp; /* multicast group associated*/ mifi_t mf6c_parent; /* incoming IF */ struct if_set mf6c_ifset; /* set of outgoing IFs */ u_quad_t mf6c_pkt_cnt; /* pkt count for src-grp */ u_quad_t mf6c_byte_cnt; /* byte count for src-grp */ u_quad_t mf6c_wrong_if; /* wrong if for src-grp */ int mf6c_expire; /* time to clean entry up */ struct timeval mf6c_last_assert; /* last time I sent an assert*/ struct rtdetq *mf6c_stall; /* pkts waiting for route */ struct mf6c *mf6c_next; /* hash table linkage */ }; #define MF6C_INCOMPLETE_PARENT ((mifi_t)-1) /* * Argument structure used for pkt info. while upcall is made */ #ifndef _NETINET_IP_MROUTE_H_ struct rtdetq { /* XXX: rtdetq is also defined in ip_mroute.h */ struct mbuf *m; /* A copy of the packet */ struct ifnet *ifp; /* Interface pkt came in on */ #ifdef UPCALL_TIMING struct timeval t; /* Timestamp */ #endif /* UPCALL_TIMING */ struct rtdetq *next; }; #endif /* _NETINET_IP_MROUTE_H_ */ #define MF6CTBLSIZ 256 #if (MF6CTBLSIZ & (MF6CTBLSIZ - 1)) == 0 /* from sys:route.h */ #define MF6CHASHMOD(h) ((h) & (MF6CTBLSIZ - 1)) #else #define MF6CHASHMOD(h) ((h) % MF6CTBLSIZ) #endif #define MAX_UPQ6 4 /* max. no of pkts in upcall Q */ -#if defined(__FreeBSD__) && __FreeBSD__ >= 3 int ip6_mrouter_set __P((struct socket *so, struct sockopt *sopt)); int ip6_mrouter_get __P((struct socket *so, struct sockopt *sopt)); -#else -int ip6_mrouter_set __P((int, struct socket *, struct mbuf *)); -int ip6_mrouter_get __P((int, struct socket *, struct mbuf **)); -#endif int ip6_mrouter_done __P((void)); int mrt6_ioctl __P((int, caddr_t)); #endif /* _KERNEL */ #endif /* !_NETINET6_IP6_MROUTE_H_ */ Index: head/sys/netinet6/ip6_output.c =================================================================== --- head/sys/netinet6/ip6_output.c (revision 62586) +++ head/sys/netinet6/ip6_output.c (revision 62587) @@ -1,2186 +1,2207 @@ +/* $FreeBSD$ */ +/* $KAME: ip6_output.c,v 1.115 2000/07/03 13:23:28 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* * Copyright (c) 1982, 1986, 1988, 1990, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)ip_output.c 8.3 (Berkeley) 1/21/94 */ -#include "opt_ipsec.h" #include "opt_ip6fw.h" +#include "opt_inet.h" +#include "opt_inet6.h" +#include "opt_ipsec.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include -#include -#include +#include +#include #include +#include #include #ifdef IPSEC #include +#ifdef INET6 #include -#include -#ifdef IPSEC_DEBUG -#include -#else -#define KEYDEBUG(lev,arg) #endif +#include #endif /* IPSEC */ -#include "loop.h" - #include #ifdef IPV6FIREWALL #include #endif static MALLOC_DEFINE(M_IPMOPTS, "ip6_moptions", "internet multicast options"); struct ip6_exthdrs { - struct mbuf *ip6e_ip6; - struct mbuf *ip6e_hbh; - struct mbuf *ip6e_dest1; - struct mbuf *ip6e_rthdr; - struct mbuf *ip6e_dest2; + struct mbuf *ip6e_ip6; + struct mbuf *ip6e_hbh; + struct mbuf *ip6e_dest1; + struct mbuf *ip6e_rthdr; + struct mbuf *ip6e_dest2; }; -static int ip6_pcbopts __P((struct ip6_pktopts **, struct mbuf *, +static int ip6_pcbopts __P((struct ip6_pktopts **, struct mbuf *, struct socket *, struct sockopt *sopt)); -static int ip6_setmoptions __P((int, struct ip6_moptions **, struct mbuf *)); -static int ip6_getmoptions __P((int, struct ip6_moptions *, struct mbuf **)); -static int ip6_copyexthdr __P((struct mbuf **, caddr_t, int)); -static int ip6_insertfraghdr __P((struct mbuf *, struct mbuf *, int, +static int ip6_setmoptions __P((int, struct ip6_moptions **, struct mbuf *)); +static int ip6_getmoptions __P((int, struct ip6_moptions *, struct mbuf **)); +static int ip6_copyexthdr __P((struct mbuf **, caddr_t, int)); +static int ip6_insertfraghdr __P((struct mbuf *, struct mbuf *, int, struct ip6_frag **)); -static int ip6_insert_jumboopt __P((struct ip6_exthdrs *, u_int32_t)); -static int ip6_splithdr __P((struct mbuf *, struct ip6_exthdrs *)); +static int ip6_insert_jumboopt __P((struct ip6_exthdrs *, u_int32_t)); +static int ip6_splithdr __P((struct mbuf *, struct ip6_exthdrs *)); /* * IP6 output. The packet in mbuf chain m contains a skeletal IP6 * header (with pri, len, nxt, hlim, src, dst). * This function may modify ver and hlim only. * The mbuf chain containing the packet will be freed. * The mbuf opt, if present, will not be freed. */ int ip6_output(m0, opt, ro, flags, im6o, ifpp) struct mbuf *m0; struct ip6_pktopts *opt; struct route_in6 *ro; int flags; struct ip6_moptions *im6o; struct ifnet **ifpp; /* XXX: just for statistics */ { struct ip6_hdr *ip6, *mhip6; - struct ifnet *ifp; + struct ifnet *ifp, *origifp; struct mbuf *m = m0; int hlen, tlen, len, off; struct route_in6 ip6route; struct sockaddr_in6 *dst; int error = 0; struct in6_ifaddr *ia; u_long mtu; u_int32_t optlen = 0, plen = 0, unfragpartlen = 0; struct ip6_exthdrs exthdrs; struct in6_addr finaldst; struct route_in6 *ro_pmtu = NULL; int hdrsplit = 0; int needipsec = 0; #ifdef IPSEC int needipsectun = 0; struct socket *so; struct secpolicy *sp = NULL; /* for AH processing. stupid to have "socket" variable in IP layer... */ - if ((flags & IPV6_SOCKINMRCVIF) != 0) { - so = (struct socket *)m->m_pkthdr.rcvif; - m->m_pkthdr.rcvif = NULL; - } else - so = NULL; + so = ipsec_getsocket(m); + ipsec_setsocket(m, NULL); ip6 = mtod(m, struct ip6_hdr *); #endif /* IPSEC */ -#define MAKE_EXTHDR(hp,mp) \ - { \ +#define MAKE_EXTHDR(hp, mp) \ + do { \ if (hp) { \ struct ip6_ext *eh = (struct ip6_ext *)(hp); \ error = ip6_copyexthdr((mp), (caddr_t)(hp), \ ((eh)->ip6e_len + 1) << 3); \ if (error) \ goto freehdrs; \ } \ - } - + } while (0) + bzero(&exthdrs, sizeof(exthdrs)); + if (opt) { /* Hop-by-Hop options header */ MAKE_EXTHDR(opt->ip6po_hbh, &exthdrs.ip6e_hbh); /* Destination options header(1st part) */ MAKE_EXTHDR(opt->ip6po_dest1, &exthdrs.ip6e_dest1); /* Routing header */ MAKE_EXTHDR(opt->ip6po_rthdr, &exthdrs.ip6e_rthdr); /* Destination options header(2nd part) */ MAKE_EXTHDR(opt->ip6po_dest2, &exthdrs.ip6e_dest2); } #ifdef IPSEC /* get a security policy for this packet */ if (so == NULL) sp = ipsec6_getpolicybyaddr(m, IPSEC_DIR_OUTBOUND, 0, &error); else sp = ipsec6_getpolicybysock(m, IPSEC_DIR_OUTBOUND, so, &error); if (sp == NULL) { ipsec6stat.out_inval++; - goto bad; + goto freehdrs; } error = 0; /* check policy */ switch (sp->policy) { case IPSEC_POLICY_DISCARD: /* * This packet is just discarded. */ ipsec6stat.out_polvio++; - goto bad; + goto freehdrs; case IPSEC_POLICY_BYPASS: case IPSEC_POLICY_NONE: /* no need to do IPsec. */ needipsec = 0; break; case IPSEC_POLICY_IPSEC: if (sp->req == NULL) { - /* XXX should be panic ? */ - printf("ip6_output: No IPsec request specified.\n"); - error = EINVAL; - goto bad; + /* acquire a policy */ + error = key_spdacquire(sp); + goto freehdrs; } needipsec = 1; break; case IPSEC_POLICY_ENTRUST: default: printf("ip6_output: Invalid policy found. %d\n", sp->policy); } #endif /* IPSEC */ /* * Calculate the total length of the extension header chain. * Keep the length of the unfragmentable part for fragmentation. */ optlen = 0; if (exthdrs.ip6e_hbh) optlen += exthdrs.ip6e_hbh->m_len; if (exthdrs.ip6e_dest1) optlen += exthdrs.ip6e_dest1->m_len; if (exthdrs.ip6e_rthdr) optlen += exthdrs.ip6e_rthdr->m_len; unfragpartlen = optlen + sizeof(struct ip6_hdr); /* NOTE: we don't add AH/ESP length here. do that later. */ if (exthdrs.ip6e_dest2) optlen += exthdrs.ip6e_dest2->m_len; /* * If we need IPsec, or there is at least one extension header, * separate IP6 header from the payload. */ if ((needipsec || optlen) && !hdrsplit) { if ((error = ip6_splithdr(m, &exthdrs)) != 0) { m = NULL; goto freehdrs; } m = exthdrs.ip6e_ip6; hdrsplit++; } /* adjust pointer */ ip6 = mtod(m, struct ip6_hdr *); /* adjust mbuf packet header length */ m->m_pkthdr.len += optlen; plen = m->m_pkthdr.len - sizeof(*ip6); /* If this is a jumbo payload, insert a jumbo payload option. */ if (plen > IPV6_MAXPACKET) { if (!hdrsplit) { if ((error = ip6_splithdr(m, &exthdrs)) != 0) { m = NULL; goto freehdrs; } m = exthdrs.ip6e_ip6; hdrsplit++; } /* adjust pointer */ ip6 = mtod(m, struct ip6_hdr *); if ((error = ip6_insert_jumboopt(&exthdrs, plen)) != 0) goto freehdrs; ip6->ip6_plen = 0; } else ip6->ip6_plen = htons(plen); /* * Concatenate headers and fill in next header fields. * Here we have, on "m" * IPv6 payload * and we insert headers accordingly. Finally, we should be getting: * IPv6 hbh dest1 rthdr ah* [esp* dest2 payload] * * during the header composing process, "m" points to IPv6 header. * "mprev" points to an extension header prior to esp. */ { u_char *nexthdrp = &ip6->ip6_nxt; struct mbuf *mprev = m; /* * we treat dest2 specially. this makes IPsec processing * much easier. * * result: IPv6 dest2 payload * m and mprev will point to IPv6 header. */ if (exthdrs.ip6e_dest2) { if (!hdrsplit) panic("assumption failed: hdr not split"); exthdrs.ip6e_dest2->m_next = m->m_next; m->m_next = exthdrs.ip6e_dest2; *mtod(exthdrs.ip6e_dest2, u_char *) = ip6->ip6_nxt; ip6->ip6_nxt = IPPROTO_DSTOPTS; } -#define MAKE_CHAIN(m,mp,p,i)\ - {\ +#define MAKE_CHAIN(m, mp, p, i)\ + do {\ if (m) {\ if (!hdrsplit) \ panic("assumption failed: hdr not split"); \ *mtod((m), u_char *) = *(p);\ *(p) = (i);\ p = mtod((m), u_char *);\ (m)->m_next = (mp)->m_next;\ (mp)->m_next = (m);\ (mp) = (m);\ }\ - } + } while (0) /* * result: IPv6 hbh dest1 rthdr dest2 payload * m will point to IPv6 header. mprev will point to the * extension header prior to dest2 (rthdr in the above case). */ MAKE_CHAIN(exthdrs.ip6e_hbh, mprev, nexthdrp, IPPROTO_HOPOPTS); MAKE_CHAIN(exthdrs.ip6e_dest1, mprev, nexthdrp, IPPROTO_DSTOPTS); MAKE_CHAIN(exthdrs.ip6e_rthdr, mprev, nexthdrp, IPPROTO_ROUTING); #ifdef IPSEC if (!needipsec) goto skip_ipsec2; /* * pointers after IPsec headers are not valid any more. * other pointers need a great care too. * (IPsec routines should not mangle mbufs prior to AH/ESP) */ exthdrs.ip6e_dest2 = NULL; { struct ip6_rthdr *rh = NULL; int segleft_org = 0; struct ipsec_output_state state; if (exthdrs.ip6e_rthdr) { rh = mtod(exthdrs.ip6e_rthdr, struct ip6_rthdr *); segleft_org = rh->ip6r_segleft; rh->ip6r_segleft = 0; } bzero(&state, sizeof(state)); state.m = m; error = ipsec6_output_trans(&state, nexthdrp, mprev, sp, flags, &needipsectun); m = state.m; if (error) { /* mbuf is already reclaimed in ipsec6_output_trans. */ m = NULL; switch (error) { case EHOSTUNREACH: case ENETUNREACH: case EMSGSIZE: case ENOBUFS: case ENOMEM: break; default: printf("ip6_output (ipsec): error code %d\n", error); /*fall through*/ case ENOENT: /* don't show these error codes to the user */ error = 0; break; } goto bad; } if (exthdrs.ip6e_rthdr) { /* ah6_output doesn't modify mbuf chain */ rh->ip6r_segleft = segleft_org; } } skip_ipsec2:; #endif } /* * If there is a routing header, replace destination address field * with the first hop of the routing header. */ if (exthdrs.ip6e_rthdr) { struct ip6_rthdr *rh = (struct ip6_rthdr *)(mtod(exthdrs.ip6e_rthdr, struct ip6_rthdr *)); struct ip6_rthdr0 *rh0; finaldst = ip6->ip6_dst; switch(rh->ip6r_type) { case IPV6_RTHDR_TYPE_0: rh0 = (struct ip6_rthdr0 *)rh; ip6->ip6_dst = rh0->ip6r0_addr[0]; bcopy((caddr_t)&rh0->ip6r0_addr[1], (caddr_t)&rh0->ip6r0_addr[0], sizeof(struct in6_addr)*(rh0->ip6r0_segleft - 1) ); rh0->ip6r0_addr[rh0->ip6r0_segleft - 1] = finaldst; break; default: /* is it possible? */ error = EINVAL; goto bad; } } /* Source address validation */ if (IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src) && (flags & IPV6_DADOUTPUT) == 0) { error = EOPNOTSUPP; ip6stat.ip6s_badscope++; goto bad; } if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_src)) { error = EOPNOTSUPP; ip6stat.ip6s_badscope++; goto bad; } ip6stat.ip6s_localout++; /* * Route packet. */ if (ro == 0) { ro = &ip6route; bzero((caddr_t)ro, sizeof(*ro)); } ro_pmtu = ro; if (opt && opt->ip6po_rthdr) ro = &opt->ip6po_route; dst = (struct sockaddr_in6 *)&ro->ro_dst; /* * If there is a cached route, * check that it is to the same destination * and is still up. If not, free it and try again. */ if (ro->ro_rt && ((ro->ro_rt->rt_flags & RTF_UP) == 0 || !IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &ip6->ip6_dst))) { RTFREE(ro->ro_rt); ro->ro_rt = (struct rtentry *)0; } if (ro->ro_rt == 0) { bzero(dst, sizeof(*dst)); dst->sin6_family = AF_INET6; dst->sin6_len = sizeof(struct sockaddr_in6); dst->sin6_addr = ip6->ip6_dst; +#ifdef SCOPEDROUTING + /* XXX: sin6_scope_id should already be fixed at this point */ + if (IN6_IS_SCOPE_LINKLOCAL(&dst->sin6_addr)) + dst->sin6_scope_id = ntohs(dst->sin6_addr.s6_addr16[1]); +#endif } #ifdef IPSEC if (needipsec && needipsectun) { struct ipsec_output_state state; /* * All the extension headers will become inaccessible * (since they can be encrypted). * Don't panic, we need no more updates to extension headers * on inner IPv6 packet (since they are now encapsulated). * * IPv6 [ESP|AH] IPv6 [extension headers] payload */ bzero(&exthdrs, sizeof(exthdrs)); exthdrs.ip6e_ip6 = m; bzero(&state, sizeof(state)); state.m = m; state.ro = (struct route *)ro; state.dst = (struct sockaddr *)dst; error = ipsec6_output_tunnel(&state, sp, flags); m = state.m; ro = (struct route_in6 *)state.ro; dst = (struct sockaddr_in6 *)state.dst; if (error) { /* mbuf is already reclaimed in ipsec6_output_tunnel. */ m0 = m = NULL; m = NULL; switch (error) { case EHOSTUNREACH: case ENETUNREACH: case EMSGSIZE: case ENOBUFS: case ENOMEM: break; default: printf("ip6_output (ipsec): error code %d\n", error); /*fall through*/ case ENOENT: /* don't show these error codes to the user */ error = 0; break; } goto bad; } exthdrs.ip6e_ip6 = m; } -#endif /*IPESC*/ +#endif /*IPSEC*/ if (!IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { /* Unicast */ #define ifatoia6(ifa) ((struct in6_ifaddr *)(ifa)) #define sin6tosa(sin6) ((struct sockaddr *)(sin6)) /* xxx * interface selection comes here * if an interface is specified from an upper layer, * ifp must point it. */ - if (ro->ro_rt == 0) + if (ro->ro_rt == 0) { + /* + * non-bsdi always clone routes, if parent is + * PRF_CLONING. + */ rtalloc((struct route *)ro); + } if (ro->ro_rt == 0) { ip6stat.ip6s_noroute++; error = EHOSTUNREACH; /* XXX in6_ifstat_inc(ifp, ifs6_out_discard); */ goto bad; } ia = ifatoia6(ro->ro_rt->rt_ifa); ifp = ro->ro_rt->rt_ifp; ro->ro_rt->rt_use++; if (ro->ro_rt->rt_flags & RTF_GATEWAY) dst = (struct sockaddr_in6 *)ro->ro_rt->rt_gateway; m->m_flags &= ~(M_BCAST | M_MCAST); /* just in case */ in6_ifstat_inc(ifp, ifs6_out_request); /* - * Check if there is the outgoing interface conflicts with - * the interface specified by ifi6_ifindex(if specified). + * Check if the outgoing interface conflicts with + * the interface specified by ifi6_ifindex (if specified). * Note that loopback interface is always okay. - * (this happens when we are sending packet toward my - * interface) + * (this may happen when we are sending a packet to one of + * our own addresses.) */ if (opt && opt->ip6po_pktinfo && opt->ip6po_pktinfo->ipi6_ifindex) { if (!(ifp->if_flags & IFF_LOOPBACK) && ifp->if_index != opt->ip6po_pktinfo->ipi6_ifindex) { ip6stat.ip6s_noroute++; in6_ifstat_inc(ifp, ifs6_out_discard); error = EHOSTUNREACH; goto bad; } } if (opt && opt->ip6po_hlim != -1) ip6->ip6_hlim = opt->ip6po_hlim & 0xff; } else { /* Multicast */ struct in6_multi *in6m; m->m_flags = (m->m_flags & ~M_BCAST) | M_MCAST; /* * See if the caller provided any multicast options */ ifp = NULL; if (im6o != NULL) { ip6->ip6_hlim = im6o->im6o_multicast_hlim; if (im6o->im6o_multicast_ifp != NULL) ifp = im6o->im6o_multicast_ifp; } else ip6->ip6_hlim = ip6_defmcasthlim; /* * See if the caller provided the outgoing interface * as an ancillary data. * Boundary check for ifindex is assumed to be already done. */ if (opt && opt->ip6po_pktinfo && opt->ip6po_pktinfo->ipi6_ifindex) ifp = ifindex2ifnet[opt->ip6po_pktinfo->ipi6_ifindex]; /* * If the destination is a node-local scope multicast, * the packet should be loop-backed only. */ if (IN6_IS_ADDR_MC_NODELOCAL(&ip6->ip6_dst)) { /* * If the outgoing interface is already specified, * it should be a loopback interface. */ if (ifp && (ifp->if_flags & IFF_LOOPBACK) == 0) { ip6stat.ip6s_badscope++; error = ENETUNREACH; /* XXX: better error? */ /* XXX correct ifp? */ in6_ifstat_inc(ifp, ifs6_out_discard); goto bad; } else { ifp = &loif[0]; } } if (opt && opt->ip6po_hlim != -1) ip6->ip6_hlim = opt->ip6po_hlim & 0xff; /* * If caller did not provide an interface lookup a * default in the routing table. This is either a * default for the speicfied group (i.e. a host * route), or a multicast default (a route for the * ``net'' ff00::/8). */ if (ifp == NULL) { if (ro->ro_rt == 0) { ro->ro_rt = rtalloc1((struct sockaddr *) &ro->ro_dst, 0, 0UL); } if (ro->ro_rt == 0) { ip6stat.ip6s_noroute++; error = EHOSTUNREACH; /* XXX in6_ifstat_inc(ifp, ifs6_out_discard) */ goto bad; } ia = ifatoia6(ro->ro_rt->rt_ifa); ifp = ro->ro_rt->rt_ifp; ro->ro_rt->rt_use++; } if ((flags & IPV6_FORWARDING) == 0) in6_ifstat_inc(ifp, ifs6_out_request); in6_ifstat_inc(ifp, ifs6_out_mcast); /* * Confirm that the outgoing interface supports multicast. */ if ((ifp->if_flags & IFF_MULTICAST) == 0) { ip6stat.ip6s_noroute++; in6_ifstat_inc(ifp, ifs6_out_discard); error = ENETUNREACH; goto bad; } IN6_LOOKUP_MULTI(ip6->ip6_dst, ifp, in6m); if (in6m != NULL && (im6o == NULL || im6o->im6o_multicast_loop)) { /* * If we belong to the destination multicast group * on the outgoing interface, and the caller did not * forbid loopback, loop back a copy. */ ip6_mloopback(ifp, m, dst); } else { /* * If we are acting as a multicast router, perform * multicast forwarding as if the packet had just * arrived on the interface to which we are about * to send. The multicast forwarding function * recursively calls this function, using the * IPV6_FORWARDING flag to prevent infinite recursion. * * Multicasts that are looped back by ip6_mloopback(), * above, will be forwarded by the ip6_input() routine, * if necessary. */ if (ip6_mrouter && (flags & IPV6_FORWARDING) == 0) { - if (ip6_mforward(ip6, ifp, m) != NULL) { + if (ip6_mforward(ip6, ifp, m) != 0) { m_freem(m); goto done; } } } /* * Multicasts with a hoplimit of zero may be looped back, * above, but must not be transmitted on a network. * Also, multicasts addressed to the loopback interface * are not sent -- the above call to ip6_mloopback() will * loop back a copy if this host actually belongs to the * destination group on the loopback interface. */ if (ip6->ip6_hlim == 0 || (ifp->if_flags & IFF_LOOPBACK)) { m_freem(m); goto done; } } /* * Fill the outgoing inteface to tell the upper layer * to increment per-interface statistics. */ if (ifpp) *ifpp = ifp; /* * Determine path MTU. */ if (ro_pmtu != ro) { /* The first hop and the final destination may differ. */ struct sockaddr_in6 *sin6_fin = (struct sockaddr_in6 *)&ro_pmtu->ro_dst; if (ro_pmtu->ro_rt && ((ro->ro_rt->rt_flags & RTF_UP) == 0 || !IN6_ARE_ADDR_EQUAL(&sin6_fin->sin6_addr, &finaldst))) { RTFREE(ro_pmtu->ro_rt); ro_pmtu->ro_rt = (struct rtentry *)0; } if (ro_pmtu->ro_rt == 0) { bzero(sin6_fin, sizeof(*sin6_fin)); sin6_fin->sin6_family = AF_INET6; sin6_fin->sin6_len = sizeof(struct sockaddr_in6); sin6_fin->sin6_addr = finaldst; rtalloc((struct route *)ro_pmtu); } } if (ro_pmtu->ro_rt != NULL) { u_int32_t ifmtu = nd_ifinfo[ifp->if_index].linkmtu; mtu = ro_pmtu->ro_rt->rt_rmx.rmx_mtu; if (mtu > ifmtu) { /* * The MTU on the route is larger than the MTU on * the interface! This shouldn't happen, unless the * MTU of the interface has been changed after the * interface was brought up. Change the MTU in the * route to match the interface MTU (as long as the * field isn't locked). */ mtu = ifmtu; if ((ro_pmtu->ro_rt->rt_rmx.rmx_locks & RTV_MTU) == 0) ro_pmtu->ro_rt->rt_rmx.rmx_mtu = mtu; /* XXX */ } } else { mtu = nd_ifinfo[ifp->if_index].linkmtu; } - /* - * Fake link-local scope-class addresses - */ - if ((ifp->if_flags & IFF_LOOPBACK) == 0) { + /* Fake scoped addresses */ + if ((ifp->if_flags & IFF_LOOPBACK) != 0) { + /* + * If source or destination address is a scoped address, and + * the packet is going to be sent to a loopback interface, + * we should keep the original interface. + */ + + /* + * XXX: this is a very experimental and temporary solution. + * We eventually have sockaddr_in6 and use the sin6_scope_id + * field of the structure here. + * We rely on the consistency between two scope zone ids + * of source add destination, which should already be assured + * Larger scopes than link will be supported in the near + * future. + */ if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) + origifp = ifindex2ifnet[ntohs(ip6->ip6_src.s6_addr16[1])]; + else if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) + origifp = ifindex2ifnet[ntohs(ip6->ip6_dst.s6_addr16[1])]; + else + origifp = ifp; + } + else + origifp = ifp; +#ifndef FAKE_LOOPBACK_IF + if ((ifp->if_flags & IFF_LOOPBACK) == 0) +#else + if (1) +#endif + { + if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) ip6->ip6_src.s6_addr16[1] = 0; if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) ip6->ip6_dst.s6_addr16[1] = 0; } #ifdef IPV6FIREWALL /* * Check with the firewall... */ if (ip6_fw_chk_ptr) { u_short port = 0; + m->m_pkthdr.rcvif = NULL; /*XXX*/ /* If ipfw says divert, we have to just drop packet */ if ((*ip6_fw_chk_ptr)(&ip6, ifp, &port, &m)) { m_freem(m); goto done; } if (!m) { error = EACCES; goto done; } } #endif /* * If the outgoing packet contains a hop-by-hop options header, * it must be examined and processed even by the source node. * (RFC 2460, section 4.) */ if (exthdrs.ip6e_hbh) { struct ip6_hbh *hbh = mtod(exthdrs.ip6e_hbh, struct ip6_hbh *); u_int32_t dummy1; /* XXX unused */ u_int32_t dummy2; /* XXX unused */ /* * XXX: if we have to send an ICMPv6 error to the sender, * we need the M_LOOP flag since icmp6_error() expects * the IPv6 and the hop-by-hop options header are * continuous unless the flag is set. */ m->m_flags |= M_LOOP; m->m_pkthdr.rcvif = ifp; if (ip6_process_hopopts(m, (u_int8_t *)(hbh + 1), ((hbh->ip6h_len + 1) << 3) - sizeof(struct ip6_hbh), &dummy1, &dummy2) < 0) { /* m was already freed at this point */ error = EINVAL;/* better error? */ goto done; } m->m_flags &= ~M_LOOP; /* XXX */ m->m_pkthdr.rcvif = NULL; } /* * Send the packet to the outgoing interface. * If necessary, do IPv6 fragmentation before sending. */ tlen = m->m_pkthdr.len; if (tlen <= mtu #ifdef notyet /* * On any link that cannot convey a 1280-octet packet in one piece, * link-specific fragmentation and reassembly must be provided at * a layer below IPv6. [RFC 2460, sec.5] * Thus if the interface has ability of link-level fragmentation, * we can just send the packet even if the packet size is * larger than the link's MTU. * XXX: IFF_FRAGMENTABLE (or such) flag has not been defined yet... */ || ifp->if_flags & IFF_FRAGMENTABLE #endif ) { -#if defined(__NetBSD__) && defined(IFA_STATS) - if (IFA_STATS) { - struct in6_ifaddr *ia6; - ip6 = mtod(m, struct ip6_hdr *); - ia6 = in6_ifawithifp(ifp, &ip6->ip6_src); - if (ia6) { - ia->ia_ifa.ifa_data.ifad_outbytes += - m->m_pkthdr.len; - } - } -#endif - error = nd6_output(ifp, m, dst, ro->ro_rt); + error = nd6_output(ifp, origifp, m, dst, ro->ro_rt); goto done; } else if (mtu < IPV6_MMTU) { /* * note that path MTU is never less than IPV6_MMTU * (see icmp6_input). */ error = EMSGSIZE; in6_ifstat_inc(ifp, ifs6_out_fragfail); goto bad; } else if (ip6->ip6_plen == 0) { /* jumbo payload cannot be fragmented */ error = EMSGSIZE; in6_ifstat_inc(ifp, ifs6_out_fragfail); goto bad; } else { struct mbuf **mnext, *m_frgpart; struct ip6_frag *ip6f; u_int32_t id = htonl(ip6_id++); u_char nextproto; /* * Too large for the destination or interface; * fragment if possible. * Must be able to put at least 8 bytes per fragment. */ hlen = unfragpartlen; if (mtu > IPV6_MAXPACKET) mtu = IPV6_MAXPACKET; len = (mtu - hlen - sizeof(struct ip6_frag)) & ~7; if (len < 8) { error = EMSGSIZE; in6_ifstat_inc(ifp, ifs6_out_fragfail); goto bad; } mnext = &m->m_nextpkt; /* * Change the next header field of the last header in the * unfragmentable part. */ if (exthdrs.ip6e_rthdr) { nextproto = *mtod(exthdrs.ip6e_rthdr, u_char *); *mtod(exthdrs.ip6e_rthdr, u_char *) = IPPROTO_FRAGMENT; } else if (exthdrs.ip6e_dest1) { nextproto = *mtod(exthdrs.ip6e_dest1, u_char *); *mtod(exthdrs.ip6e_dest1, u_char *) = IPPROTO_FRAGMENT; } else if (exthdrs.ip6e_hbh) { nextproto = *mtod(exthdrs.ip6e_hbh, u_char *); *mtod(exthdrs.ip6e_hbh, u_char *) = IPPROTO_FRAGMENT; } else { nextproto = ip6->ip6_nxt; ip6->ip6_nxt = IPPROTO_FRAGMENT; } /* * Loop through length of segment after first fragment, * make new header and copy data of each part and link onto chain. */ m0 = m; for (off = hlen; off < tlen; off += len) { MGETHDR(m, M_DONTWAIT, MT_HEADER); if (!m) { error = ENOBUFS; ip6stat.ip6s_odropped++; goto sendorfree; } m->m_flags = m0->m_flags & M_COPYFLAGS; *mnext = m; mnext = &m->m_nextpkt; m->m_data += max_linkhdr; mhip6 = mtod(m, struct ip6_hdr *); *mhip6 = *ip6; m->m_len = sizeof(*mhip6); error = ip6_insertfraghdr(m0, m, hlen, &ip6f); if (error) { ip6stat.ip6s_odropped++; goto sendorfree; } ip6f->ip6f_offlg = htons((u_short)((off - hlen) & ~7)); if (off + len >= tlen) len = tlen - off; else ip6f->ip6f_offlg |= IP6F_MORE_FRAG; mhip6->ip6_plen = htons((u_short)(len + hlen + sizeof(*ip6f) - sizeof(struct ip6_hdr))); if ((m_frgpart = m_copy(m0, off, len)) == 0) { error = ENOBUFS; ip6stat.ip6s_odropped++; goto sendorfree; } m_cat(m, m_frgpart); m->m_pkthdr.len = len + hlen + sizeof(*ip6f); m->m_pkthdr.rcvif = (struct ifnet *)0; ip6f->ip6f_reserved = 0; ip6f->ip6f_ident = id; ip6f->ip6f_nxt = nextproto; ip6stat.ip6s_ofragments++; in6_ifstat_inc(ifp, ifs6_out_fragcreat); } in6_ifstat_inc(ifp, ifs6_out_fragok); } /* * Remove leading garbages. */ sendorfree: m = m0->m_nextpkt; m0->m_nextpkt = 0; m_freem(m0); for (m0 = m; m; m = m0) { m0 = m->m_nextpkt; m->m_nextpkt = 0; if (error == 0) { -#if defined(__NetBSD__) && defined(IFA_STATS) - if (IFA_STATS) { - struct in6_ifaddr *ia6; - ip6 = mtod(m, struct ip6_hdr *); - ia6 = in6_ifawithifp(ifp, &ip6->ip6_src); - if (ia6) { - ia->ia_ifa.ifa_data.ifad_outbytes += - m->m_pkthdr.len; - } - } -#endif - error = nd6_output(ifp, m, dst, ro->ro_rt); + error = nd6_output(ifp, origifp, m, dst, ro->ro_rt); } else m_freem(m); } if (error == 0) ip6stat.ip6s_fragmented++; done: if (ro == &ip6route && ro->ro_rt) { /* brace necessary for RTFREE */ RTFREE(ro->ro_rt); } else if (ro_pmtu == &ip6route && ro_pmtu->ro_rt) { RTFREE(ro_pmtu->ro_rt); } #ifdef IPSEC if (sp != NULL) key_freesp(sp); #endif /* IPSEC */ return(error); freehdrs: m_freem(exthdrs.ip6e_hbh); /* m_freem will check if mbuf is 0 */ m_freem(exthdrs.ip6e_dest1); m_freem(exthdrs.ip6e_rthdr); m_freem(exthdrs.ip6e_dest2); /* fall through */ bad: m_freem(m); goto done; } static int ip6_copyexthdr(mp, hdr, hlen) struct mbuf **mp; caddr_t hdr; int hlen; { struct mbuf *m; if (hlen > MCLBYTES) return(ENOBUFS); /* XXX */ MGET(m, M_DONTWAIT, MT_DATA); if (!m) return(ENOBUFS); if (hlen > MLEN) { MCLGET(m, M_DONTWAIT); if ((m->m_flags & M_EXT) == 0) { m_free(m); return(ENOBUFS); } } m->m_len = hlen; if (hdr) bcopy(hdr, mtod(m, caddr_t), hlen); *mp = m; return(0); } /* * Insert jumbo payload option. */ static int ip6_insert_jumboopt(exthdrs, plen) struct ip6_exthdrs *exthdrs; u_int32_t plen; { struct mbuf *mopt; u_char *optbuf; #define JUMBOOPTLEN 8 /* length of jumbo payload option and padding */ /* * If there is no hop-by-hop options header, allocate new one. * If there is one but it doesn't have enough space to store the * jumbo payload option, allocate a cluster to store the whole options. * Otherwise, use it to store the options. */ if (exthdrs->ip6e_hbh == 0) { MGET(mopt, M_DONTWAIT, MT_DATA); if (mopt == 0) return(ENOBUFS); mopt->m_len = JUMBOOPTLEN; optbuf = mtod(mopt, u_char *); optbuf[1] = 0; /* = ((JUMBOOPTLEN) >> 3) - 1 */ exthdrs->ip6e_hbh = mopt; } else { struct ip6_hbh *hbh; mopt = exthdrs->ip6e_hbh; if (M_TRAILINGSPACE(mopt) < JUMBOOPTLEN) { caddr_t oldoptp = mtod(mopt, caddr_t); int oldoptlen = mopt->m_len; if (mopt->m_flags & M_EXT) return(ENOBUFS); /* XXX */ MCLGET(mopt, M_DONTWAIT); if ((mopt->m_flags & M_EXT) == 0) return(ENOBUFS); bcopy(oldoptp, mtod(mopt, caddr_t), oldoptlen); optbuf = mtod(mopt, caddr_t) + oldoptlen; mopt->m_len = oldoptlen + JUMBOOPTLEN; } else { optbuf = mtod(mopt, u_char *) + mopt->m_len; mopt->m_len += JUMBOOPTLEN; } optbuf[0] = IP6OPT_PADN; optbuf[1] = 1; /* * Adjust the header length according to the pad and * the jumbo payload option. */ hbh = mtod(mopt, struct ip6_hbh *); hbh->ip6h_len += (JUMBOOPTLEN >> 3); } /* fill in the option. */ optbuf[2] = IP6OPT_JUMBO; optbuf[3] = 4; *(u_int32_t *)&optbuf[4] = htonl(plen + JUMBOOPTLEN); /* finally, adjust the packet header length */ exthdrs->ip6e_ip6->m_pkthdr.len += JUMBOOPTLEN; return(0); #undef JUMBOOPTLEN } /* * Insert fragment header and copy unfragmentable header portions. */ static int ip6_insertfraghdr(m0, m, hlen, frghdrp) struct mbuf *m0, *m; int hlen; struct ip6_frag **frghdrp; { struct mbuf *n, *mlast; if (hlen > sizeof(struct ip6_hdr)) { n = m_copym(m0, sizeof(struct ip6_hdr), hlen - sizeof(struct ip6_hdr), M_DONTWAIT); if (n == 0) return(ENOBUFS); m->m_next = n; } else n = m; /* Search for the last mbuf of unfragmentable part. */ for (mlast = n; mlast->m_next; mlast = mlast->m_next) ; if ((mlast->m_flags & M_EXT) == 0 && - M_TRAILINGSPACE(mlast) < sizeof(struct ip6_frag)) { + M_TRAILINGSPACE(mlast) >= sizeof(struct ip6_frag)) { /* use the trailing space of the last mbuf for the fragment hdr */ *frghdrp = (struct ip6_frag *)(mtod(mlast, caddr_t) + mlast->m_len); mlast->m_len += sizeof(struct ip6_frag); m->m_pkthdr.len += sizeof(struct ip6_frag); } else { /* allocate a new mbuf for the fragment header */ struct mbuf *mfrg; MGET(mfrg, M_DONTWAIT, MT_DATA); if (mfrg == 0) return(ENOBUFS); mfrg->m_len = sizeof(struct ip6_frag); *frghdrp = mtod(mfrg, struct ip6_frag *); mlast->m_next = mfrg; } return(0); } /* * IP6 socket option processing. */ int ip6_ctloutput(so, sopt) struct socket *so; struct sockopt *sopt; { int privileged; register struct inpcb *in6p = sotoinpcb(so); int error, optval; int level, op, optname; int optlen; struct proc *p; if (sopt) { level = sopt->sopt_level; op = sopt->sopt_dir; optname = sopt->sopt_name; optlen = sopt->sopt_valsize; p = sopt->sopt_p; } else { panic("ip6_ctloutput: arg soopt is NULL"); } error = optval = 0; privileged = (p == 0 || suser(p)) ? 0 : 1; if (level == IPPROTO_IPV6) { switch (op) { case SOPT_SET: switch (optname) { case IPV6_PKTOPTIONS: { struct mbuf *m; error = soopt_getm(sopt, &m); /* XXX */ if (error != 0) break; error = soopt_mcopyin(sopt, m); /* XXX */ if (error != 0) break; return (ip6_pcbopts(&in6p->in6p_outputopts, m, so, sopt)); } case IPV6_HOPOPTS: case IPV6_DSTOPTS: if (!privileged) { error = EPERM; break; } /* fall through */ case IPV6_UNICAST_HOPS: - case IPV6_RECVOPTS: - case IPV6_RECVRETOPTS: - case IPV6_RECVDSTADDR: case IPV6_PKTINFO: case IPV6_HOPLIMIT: case IPV6_RTHDR: case IPV6_CHECKSUM: case IPV6_FAITH: case IPV6_BINDV6ONLY: if (optlen != sizeof(int)) error = EINVAL; else { error = sooptcopyin(sopt, &optval, sizeof optval, sizeof optval); if (error) break; switch (optname) { case IPV6_UNICAST_HOPS: if (optval < -1 || optval >= 256) error = EINVAL; else { /* -1 = kernel default */ in6p->in6p_hops = optval; if ((in6p->in6p_vflag & INP_IPV4) != 0) in6p->inp_ip_ttl = optval; } break; #define OPTSET(bit) \ if (optval) \ in6p->in6p_flags |= bit; \ else \ in6p->in6p_flags &= ~bit; - case IPV6_RECVOPTS: - OPTSET(IN6P_RECVOPTS); - break; - - case IPV6_RECVRETOPTS: - OPTSET(IN6P_RECVRETOPTS); - break; - - case IPV6_RECVDSTADDR: - OPTSET(IN6P_RECVDSTADDR); - break; - case IPV6_PKTINFO: OPTSET(IN6P_PKTINFO); break; case IPV6_HOPLIMIT: OPTSET(IN6P_HOPLIMIT); break; case IPV6_HOPOPTS: OPTSET(IN6P_HOPOPTS); break; case IPV6_DSTOPTS: OPTSET(IN6P_DSTOPTS); break; case IPV6_RTHDR: OPTSET(IN6P_RTHDR); break; case IPV6_CHECKSUM: in6p->in6p_cksum = optval; break; case IPV6_FAITH: OPTSET(IN6P_FAITH); break; case IPV6_BINDV6ONLY: OPTSET(IN6P_BINDV6ONLY); break; } } break; #undef OPTSET case IPV6_MULTICAST_IF: case IPV6_MULTICAST_HOPS: case IPV6_MULTICAST_LOOP: case IPV6_JOIN_GROUP: case IPV6_LEAVE_GROUP: { struct mbuf *m; if (sopt->sopt_valsize > MLEN) { error = EMSGSIZE; break; } /* XXX */ MGET(m, sopt->sopt_p ? M_WAIT : M_DONTWAIT, MT_HEADER); if (m == 0) { error = ENOBUFS; break; } m->m_len = sopt->sopt_valsize; error = sooptcopyin(sopt, mtod(m, char *), m->m_len, m->m_len); error = ip6_setmoptions(sopt->sopt_name, &in6p->in6p_moptions, m); (void)m_free(m); } break; - case IPV6_PORTRANGE: - error = sooptcopyin(sopt, &optval, sizeof optval, - sizeof optval); - if (error) - break; + case IPV6_PORTRANGE: + error = sooptcopyin(sopt, &optval, + sizeof optval, sizeof optval); + if (error) + break; - switch (optval) { - case IPV6_PORTRANGE_DEFAULT: - in6p->in6p_flags &= ~(IN6P_LOWPORT); - in6p->in6p_flags &= ~(IN6P_HIGHPORT); - break; + switch (optval) { + case IPV6_PORTRANGE_DEFAULT: + in6p->in6p_flags &= ~(IN6P_LOWPORT); + in6p->in6p_flags &= ~(IN6P_HIGHPORT); + break; - case IPV6_PORTRANGE_HIGH: - in6p->in6p_flags &= ~(IN6P_LOWPORT); - in6p->in6p_flags |= IN6P_HIGHPORT; - break; + case IPV6_PORTRANGE_HIGH: + in6p->in6p_flags &= ~(IN6P_LOWPORT); + in6p->in6p_flags |= IN6P_HIGHPORT; + break; - case IPV6_PORTRANGE_LOW: - in6p->in6p_flags &= ~(IN6P_HIGHPORT); - in6p->in6p_flags |= IN6P_LOWPORT; - break; + case IPV6_PORTRANGE_LOW: + in6p->in6p_flags &= ~(IN6P_HIGHPORT); + in6p->in6p_flags |= IN6P_LOWPORT; + break; - default: - error = EINVAL; + default: + error = EINVAL; + break; + } break; - } - break; #ifdef IPSEC case IPV6_IPSEC_POLICY: { caddr_t req = NULL; + size_t len = 0; struct mbuf *m; - if ((error = soopt_getm(sopt, &m)) - != 0) /* XXX */ + if ((error = soopt_getm(sopt, &m)) != 0) /* XXX */ break; - if ((error = soopt_mcopyin(sopt, m)) - != 0) /* XXX */ + if ((error = soopt_mcopyin(sopt, m)) != 0) /* XXX */ break; - if (m != 0) + if (m) { req = mtod(m, caddr_t); + len = m->m_len; + } error = ipsec6_set_policy(in6p, optname, req, - privileged); + len, privileged); m_freem(m); } break; #endif /* IPSEC */ #ifdef IPV6FIREWALL case IPV6_FW_ADD: case IPV6_FW_DEL: case IPV6_FW_FLUSH: case IPV6_FW_ZERO: { struct mbuf *m; struct mbuf **mp = &m; if (ip6_fw_ctl_ptr == NULL) return EINVAL; if ((error = soopt_getm(sopt, &m)) != 0) /* XXX */ break; if ((error = soopt_mcopyin(sopt, m)) != 0) /* XXX */ break; error = (*ip6_fw_ctl_ptr)(optname, mp); m = *mp; } break; #endif default: error = ENOPROTOOPT; break; } break; case SOPT_GET: switch (optname) { - case IPV6_OPTIONS: - case IPV6_RETOPTS: - error = ENOPROTOOPT; - break; - case IPV6_PKTOPTIONS: if (in6p->in6p_options) { error = soopt_mcopyout(sopt, in6p->in6p_options); } else sopt->sopt_valsize = 0; break; case IPV6_HOPOPTS: case IPV6_DSTOPTS: if (!privileged) { error = EPERM; break; } /* fall through */ case IPV6_UNICAST_HOPS: - case IPV6_RECVOPTS: - case IPV6_RECVRETOPTS: - case IPV6_RECVDSTADDR: case IPV6_PKTINFO: case IPV6_HOPLIMIT: case IPV6_RTHDR: case IPV6_CHECKSUM: case IPV6_FAITH: case IPV6_BINDV6ONLY: case IPV6_PORTRANGE: switch (optname) { case IPV6_UNICAST_HOPS: optval = in6p->in6p_hops; break; #define OPTBIT(bit) (in6p->in6p_flags & bit ? 1 : 0) - case IPV6_RECVOPTS: - optval = OPTBIT(IN6P_RECVOPTS); - break; - - case IPV6_RECVRETOPTS: - optval = OPTBIT(IN6P_RECVRETOPTS); - break; - - case IPV6_RECVDSTADDR: - optval = OPTBIT(IN6P_RECVDSTADDR); - break; - case IPV6_PKTINFO: optval = OPTBIT(IN6P_PKTINFO); break; case IPV6_HOPLIMIT: optval = OPTBIT(IN6P_HOPLIMIT); break; case IPV6_HOPOPTS: optval = OPTBIT(IN6P_HOPOPTS); break; case IPV6_DSTOPTS: optval = OPTBIT(IN6P_DSTOPTS); break; case IPV6_RTHDR: optval = OPTBIT(IN6P_RTHDR); break; case IPV6_CHECKSUM: optval = in6p->in6p_cksum; break; case IPV6_FAITH: optval = OPTBIT(IN6P_FAITH); break; case IPV6_BINDV6ONLY: optval = OPTBIT(IN6P_BINDV6ONLY); break; case IPV6_PORTRANGE: { int flags; flags = in6p->in6p_flags; if (flags & IN6P_HIGHPORT) optval = IPV6_PORTRANGE_HIGH; else if (flags & IN6P_LOWPORT) optval = IPV6_PORTRANGE_LOW; else optval = 0; break; } } error = sooptcopyout(sopt, &optval, sizeof optval); break; case IPV6_MULTICAST_IF: case IPV6_MULTICAST_HOPS: case IPV6_MULTICAST_LOOP: case IPV6_JOIN_GROUP: case IPV6_LEAVE_GROUP: { struct mbuf *m; error = ip6_getmoptions(sopt->sopt_name, in6p->in6p_moptions, &m); if (error == 0) error = sooptcopyout(sopt, mtod(m, char *), m->m_len); m_freem(m); } break; #ifdef IPSEC case IPV6_IPSEC_POLICY: { - - struct mbuf *m = NULL; caddr_t req = NULL; + size_t len = 0; + struct mbuf *m = NULL; + struct mbuf **mp = &m; - if (m != 0) + error = soopt_getm(sopt, &m); /* XXX */ + if (error != NULL) + break; + error = soopt_mcopyin(sopt, m); /* XXX */ + if (error != NULL) + break; + if (m) { req = mtod(m, caddr_t); - error = ipsec6_get_policy(in6p, req, &m); + len = m->m_len; + } + error = ipsec6_get_policy(in6p, req, len, mp); if (error == 0) error = soopt_mcopyout(sopt, m); /*XXX*/ - if (error == 0) - m_freem(m); + m_freem(m); break; } #endif /* IPSEC */ #ifdef IPV6FIREWALL case IPV6_FW_GET: { struct mbuf *m; struct mbuf **mp = &m; if (ip6_fw_ctl_ptr == NULL) { return EINVAL; } error = (*ip6_fw_ctl_ptr)(optname, mp); if (error == 0) error = soopt_mcopyout(sopt, m); /* XXX */ if (m) m_freem(m); } break; #endif default: error = ENOPROTOOPT; break; } break; } } else { error = EINVAL; } return(error); } /* * Set up IP6 options in pcb for insertion in output packets. * Store in mbuf with pointer in pcbopt, adding pseudo-option * with destination address if source routed. */ static int ip6_pcbopts(pktopt, m, so, sopt) struct ip6_pktopts **pktopt; register struct mbuf *m; struct socket *so; struct sockopt *sopt; { register struct ip6_pktopts *opt = *pktopt; int error = 0; struct proc *p = sopt->sopt_p; int priv = 0; /* turn off any old options. */ if (opt) { if (opt->ip6po_m) (void)m_free(opt->ip6po_m); } else opt = malloc(sizeof(*opt), M_IP6OPT, M_WAITOK); *pktopt = 0; if (!m || m->m_len == 0) { /* * Only turning off any previous options. */ if (opt) free(opt, M_IP6OPT); if (m) (void)m_free(m); return(0); } /* set options specified by user. */ if (p && !suser(p)) priv = 1; if ((error = ip6_setpktoptions(m, opt, priv)) != 0) { (void)m_free(m); return(error); } *pktopt = opt; return(0); } /* * Set the IP6 multicast options in response to user setsockopt(). */ static int ip6_setmoptions(optname, im6op, m) int optname; struct ip6_moptions **im6op; struct mbuf *m; { int error = 0; u_int loop, ifindex; struct ipv6_mreq *mreq; struct ifnet *ifp; struct ip6_moptions *im6o = *im6op; struct route_in6 ro; struct sockaddr_in6 *dst; struct in6_multi_mship *imm; struct proc *p = curproc; /* XXX */ if (im6o == NULL) { /* * No multicast option buffer attached to the pcb; * allocate one and initialize to default values. */ im6o = (struct ip6_moptions *) malloc(sizeof(*im6o), M_IPMOPTS, M_WAITOK); if (im6o == NULL) return(ENOBUFS); *im6op = im6o; im6o->im6o_multicast_ifp = NULL; im6o->im6o_multicast_hlim = ip6_defmcasthlim; im6o->im6o_multicast_loop = IPV6_DEFAULT_MULTICAST_LOOP; LIST_INIT(&im6o->im6o_memberships); } switch (optname) { case IPV6_MULTICAST_IF: /* * Select the interface for outgoing multicast packets. */ if (m == NULL || m->m_len != sizeof(u_int)) { error = EINVAL; break; } ifindex = *(mtod(m, u_int *)); if (ifindex < 0 || if_index < ifindex) { error = ENXIO; /* XXX EINVAL? */ break; } ifp = ifindex2ifnet[ifindex]; if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) { error = EADDRNOTAVAIL; break; } im6o->im6o_multicast_ifp = ifp; break; case IPV6_MULTICAST_HOPS: { /* * Set the IP6 hoplimit for outgoing multicast packets. */ int optval; if (m == NULL || m->m_len != sizeof(int)) { error = EINVAL; break; } optval = *(mtod(m, u_int *)); if (optval < -1 || optval >= 256) error = EINVAL; else if (optval == -1) im6o->im6o_multicast_hlim = ip6_defmcasthlim; else im6o->im6o_multicast_hlim = optval; break; } case IPV6_MULTICAST_LOOP: /* * Set the loopback flag for outgoing multicast packets. * Must be zero or one. */ if (m == NULL || m->m_len != sizeof(u_int) || (loop = *(mtod(m, u_int *))) > 1) { error = EINVAL; break; } im6o->im6o_multicast_loop = loop; break; case IPV6_JOIN_GROUP: /* * Add a multicast group membership. * Group must be a valid IP6 multicast address. */ if (m == NULL || m->m_len != sizeof(struct ipv6_mreq)) { error = EINVAL; break; } mreq = mtod(m, struct ipv6_mreq *); if (IN6_IS_ADDR_UNSPECIFIED(&mreq->ipv6mr_multiaddr)) { /* * We use the unspecified address to specify to accept * all multicast addresses. Only super user is allowed * to do this. */ - if (suser(p)) { + if (suser(p)) + { error = EACCES; break; } } else if (!IN6_IS_ADDR_MULTICAST(&mreq->ipv6mr_multiaddr)) { error = EINVAL; break; } /* * If the interface is specified, validate it. */ if (mreq->ipv6mr_interface < 0 || if_index < mreq->ipv6mr_interface) { error = ENXIO; /* XXX EINVAL? */ break; } /* * If no interface was explicitly specified, choose an * appropriate one according to the given multicast address. */ if (mreq->ipv6mr_interface == 0) { /* * If the multicast address is in node-local scope, * the interface should be a loopback interface. * Otherwise, look up the routing table for the * address, and choose the outgoing interface. * XXX: is it a good approach? */ if (IN6_IS_ADDR_MC_NODELOCAL(&mreq->ipv6mr_multiaddr)) { ifp = &loif[0]; } else { ro.ro_rt = NULL; dst = (struct sockaddr_in6 *)&ro.ro_dst; bzero(dst, sizeof(*dst)); dst->sin6_len = sizeof(struct sockaddr_in6); dst->sin6_family = AF_INET6; dst->sin6_addr = mreq->ipv6mr_multiaddr; rtalloc((struct route *)&ro); if (ro.ro_rt == NULL) { error = EADDRNOTAVAIL; break; } ifp = ro.ro_rt->rt_ifp; rtfree(ro.ro_rt); } } else ifp = ifindex2ifnet[mreq->ipv6mr_interface]; /* * See if we found an interface, and confirm that it * supports multicast */ if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) { error = EADDRNOTAVAIL; break; } /* * Put interface index into the multicast address, * if the address has link-local scope. */ if (IN6_IS_ADDR_MC_LINKLOCAL(&mreq->ipv6mr_multiaddr)) { mreq->ipv6mr_multiaddr.s6_addr16[1] = htons(mreq->ipv6mr_interface); } /* * See if the membership already exists. */ - LIST_FOREACH(imm, &im6o->im6o_memberships, i6mm_chain) + for (imm = im6o->im6o_memberships.lh_first; + imm != NULL; imm = imm->i6mm_chain.le_next) if (imm->i6mm_maddr->in6m_ifp == ifp && IN6_ARE_ADDR_EQUAL(&imm->i6mm_maddr->in6m_addr, &mreq->ipv6mr_multiaddr)) break; if (imm != NULL) { error = EADDRINUSE; break; } /* * Everything looks good; add a new record to the multicast * address list for the given interface. */ imm = malloc(sizeof(*imm), M_IPMADDR, M_WAITOK); if (imm == NULL) { error = ENOBUFS; break; } if ((imm->i6mm_maddr = in6_addmulti(&mreq->ipv6mr_multiaddr, ifp, &error)) == NULL) { free(imm, M_IPMADDR); break; } LIST_INSERT_HEAD(&im6o->im6o_memberships, imm, i6mm_chain); break; case IPV6_LEAVE_GROUP: /* * Drop a multicast group membership. * Group must be a valid IP6 multicast address. */ if (m == NULL || m->m_len != sizeof(struct ipv6_mreq)) { error = EINVAL; break; } mreq = mtod(m, struct ipv6_mreq *); if (IN6_IS_ADDR_UNSPECIFIED(&mreq->ipv6mr_multiaddr)) { if (suser(p)) { error = EACCES; break; } } else if (!IN6_IS_ADDR_MULTICAST(&mreq->ipv6mr_multiaddr)) { error = EINVAL; break; } /* * If an interface address was specified, get a pointer * to its ifnet structure. */ if (mreq->ipv6mr_interface < 0 || if_index < mreq->ipv6mr_interface) { error = ENXIO; /* XXX EINVAL? */ break; } ifp = ifindex2ifnet[mreq->ipv6mr_interface]; /* * Put interface index into the multicast address, * if the address has link-local scope. */ if (IN6_IS_ADDR_MC_LINKLOCAL(&mreq->ipv6mr_multiaddr)) { mreq->ipv6mr_multiaddr.s6_addr16[1] = htons(mreq->ipv6mr_interface); } /* * Find the membership in the membership list. */ - LIST_FOREACH(imm, &im6o->im6o_memberships, i6mm_chain) { + for (imm = im6o->im6o_memberships.lh_first; + imm != NULL; imm = imm->i6mm_chain.le_next) { if ((ifp == NULL || imm->i6mm_maddr->in6m_ifp == ifp) && IN6_ARE_ADDR_EQUAL(&imm->i6mm_maddr->in6m_addr, &mreq->ipv6mr_multiaddr)) break; } if (imm == NULL) { /* Unable to resolve interface */ error = EADDRNOTAVAIL; break; } /* * Give up the multicast address record to which the * membership points. */ LIST_REMOVE(imm, i6mm_chain); in6_delmulti(imm->i6mm_maddr); free(imm, M_IPMADDR); break; default: error = EOPNOTSUPP; break; } /* * If all options have default values, no need to keep the mbuf. */ if (im6o->im6o_multicast_ifp == NULL && im6o->im6o_multicast_hlim == ip6_defmcasthlim && im6o->im6o_multicast_loop == IPV6_DEFAULT_MULTICAST_LOOP && - LIST_EMPTY(&im6o->im6o_memberships)) { + im6o->im6o_memberships.lh_first == NULL) { free(*im6op, M_IPMOPTS); *im6op = NULL; } return(error); } /* * Return the IP6 multicast options in response to user getsockopt(). */ static int ip6_getmoptions(optname, im6o, mp) int optname; register struct ip6_moptions *im6o; register struct mbuf **mp; { u_int *hlim, *loop, *ifindex; *mp = m_get(M_WAIT, MT_HEADER); /*XXX*/ switch (optname) { case IPV6_MULTICAST_IF: ifindex = mtod(*mp, u_int *); (*mp)->m_len = sizeof(u_int); if (im6o == NULL || im6o->im6o_multicast_ifp == NULL) *ifindex = 0; else *ifindex = im6o->im6o_multicast_ifp->if_index; return(0); case IPV6_MULTICAST_HOPS: hlim = mtod(*mp, u_int *); (*mp)->m_len = sizeof(u_int); if (im6o == NULL) *hlim = ip6_defmcasthlim; else *hlim = im6o->im6o_multicast_hlim; return(0); case IPV6_MULTICAST_LOOP: loop = mtod(*mp, u_int *); (*mp)->m_len = sizeof(u_int); if (im6o == NULL) *loop = ip6_defmcasthlim; else *loop = im6o->im6o_multicast_loop; return(0); default: return(EOPNOTSUPP); } } /* * Discard the IP6 multicast options. */ void ip6_freemoptions(im6o) register struct ip6_moptions *im6o; { struct in6_multi_mship *imm; if (im6o == NULL) return; - while ((imm = LIST_FIRST(&im6o->im6o_memberships)) != NULL) { + while ((imm = im6o->im6o_memberships.lh_first) != NULL) { LIST_REMOVE(imm, i6mm_chain); if (imm->i6mm_maddr) in6_delmulti(imm->i6mm_maddr); free(imm, M_IPMADDR); } free(im6o, M_IPMOPTS); } /* * Set IPv6 outgoing packet options based on advanced API. */ int ip6_setpktoptions(control, opt, priv) struct mbuf *control; struct ip6_pktopts *opt; int priv; { register struct cmsghdr *cm = 0; if (control == 0 || opt == 0) return(EINVAL); bzero(opt, sizeof(*opt)); opt->ip6po_hlim = -1; /* -1 means to use default hop limit */ /* * XXX: Currently, we assume all the optional information is stored * in a single mbuf. */ if (control->m_next) return(EINVAL); - opt->ip6po_m = control; - for (; control->m_len; control->m_data += ALIGN(cm->cmsg_len), control->m_len -= ALIGN(cm->cmsg_len)) { cm = mtod(control, struct cmsghdr *); if (cm->cmsg_len == 0 || cm->cmsg_len > control->m_len) return(EINVAL); if (cm->cmsg_level != IPPROTO_IPV6) continue; switch(cm->cmsg_type) { case IPV6_PKTINFO: if (cm->cmsg_len != CMSG_LEN(sizeof(struct in6_pktinfo))) return(EINVAL); opt->ip6po_pktinfo = (struct in6_pktinfo *)CMSG_DATA(cm); if (opt->ip6po_pktinfo->ipi6_ifindex && IN6_IS_ADDR_LINKLOCAL(&opt->ip6po_pktinfo->ipi6_addr)) opt->ip6po_pktinfo->ipi6_addr.s6_addr16[1] = htons(opt->ip6po_pktinfo->ipi6_ifindex); if (opt->ip6po_pktinfo->ipi6_ifindex > if_index || opt->ip6po_pktinfo->ipi6_ifindex < 0) { return(ENXIO); } if (!IN6_IS_ADDR_UNSPECIFIED(&opt->ip6po_pktinfo->ipi6_addr)) { struct ifaddr *ia; struct sockaddr_in6 sin6; bzero(&sin6, sizeof(sin6)); sin6.sin6_len = sizeof(sin6); sin6.sin6_family = AF_INET6; sin6.sin6_addr = opt->ip6po_pktinfo->ipi6_addr; ia = ifa_ifwithaddr(sin6tosa(&sin6)); if (ia == NULL || (opt->ip6po_pktinfo->ipi6_ifindex && (ia->ifa_ifp->if_index != opt->ip6po_pktinfo->ipi6_ifindex))) { return(EADDRNOTAVAIL); } /* * Check if the requested source address is * indeed a unicast address assigned to the * node. */ if (IN6_IS_ADDR_MULTICAST(&opt->ip6po_pktinfo->ipi6_addr)) return(EADDRNOTAVAIL); } break; case IPV6_HOPLIMIT: if (cm->cmsg_len != CMSG_LEN(sizeof(int))) return(EINVAL); opt->ip6po_hlim = *(int *)CMSG_DATA(cm); if (opt->ip6po_hlim < -1 || opt->ip6po_hlim > 255) return(EINVAL); break; case IPV6_NEXTHOP: if (!priv) return(EPERM); if (cm->cmsg_len < sizeof(u_char) || cm->cmsg_len < CMSG_LEN(*CMSG_DATA(cm))) return(EINVAL); opt->ip6po_nexthop = (struct sockaddr *)CMSG_DATA(cm); break; case IPV6_HOPOPTS: if (cm->cmsg_len < CMSG_LEN(sizeof(struct ip6_hbh))) return(EINVAL); opt->ip6po_hbh = (struct ip6_hbh *)CMSG_DATA(cm); if (cm->cmsg_len != CMSG_LEN((opt->ip6po_hbh->ip6h_len + 1) << 3)) return(EINVAL); break; case IPV6_DSTOPTS: if (cm->cmsg_len < CMSG_LEN(sizeof(struct ip6_dest))) return(EINVAL); /* * If there is no routing header yet, the destination * options header should be put on the 1st part. * Otherwise, the header should be on the 2nd part. * (See RFC 2460, section 4.1) */ if (opt->ip6po_rthdr == NULL) { opt->ip6po_dest1 = (struct ip6_dest *)CMSG_DATA(cm); if (cm->cmsg_len != CMSG_LEN((opt->ip6po_dest1->ip6d_len + 1) << 3)) return(EINVAL); } else { opt->ip6po_dest2 = (struct ip6_dest *)CMSG_DATA(cm); if (cm->cmsg_len != CMSG_LEN((opt->ip6po_dest2->ip6d_len + 1) << 3)) return(EINVAL); } break; case IPV6_RTHDR: if (cm->cmsg_len < CMSG_LEN(sizeof(struct ip6_rthdr))) return(EINVAL); opt->ip6po_rthdr = (struct ip6_rthdr *)CMSG_DATA(cm); if (cm->cmsg_len != CMSG_LEN((opt->ip6po_rthdr->ip6r_len + 1) << 3)) return(EINVAL); switch(opt->ip6po_rthdr->ip6r_type) { case IPV6_RTHDR_TYPE_0: if (opt->ip6po_rthdr->ip6r_segleft == 0) return(EINVAL); break; default: return(EINVAL); } break; default: return(ENOPROTOOPT); } } return(0); } /* * Routine called from ip6_output() to loop back a copy of an IP6 multicast * packet to the input queue of a specified interface. Note that this * calls the output routine of the loopback "driver", but with an interface * pointer that might NOT be &loif -- easier than replicating that code here. */ void ip6_mloopback(ifp, m, dst) struct ifnet *ifp; register struct mbuf *m; register struct sockaddr_in6 *dst; { - struct mbuf *copym; + struct mbuf *copym; + struct ip6_hdr *ip6; copym = m_copy(m, 0, M_COPYALL); - if (copym != NULL) { - (void)if_simloop(ifp, copym, dst->sin6_family, 0); + if (copym == NULL) + return; + + /* + * Make sure to deep-copy IPv6 header portion in case the data + * is in an mbuf cluster, so that we can safely override the IPv6 + * header portion later. + */ + if ((copym->m_flags & M_EXT) != 0 || + copym->m_len < sizeof(struct ip6_hdr)) { + copym = m_pullup(copym, sizeof(struct ip6_hdr)); + if (copym == NULL) + return; } + +#ifdef DIAGNOSTIC + if (copym->m_len < sizeof(*ip6)) { + m_freem(copym); + return; + } +#endif + +#ifndef FAKE_LOOPBACK_IF + if ((ifp->if_flags & IFF_LOOPBACK) == 0) +#else + if (1) +#endif + { + ip6 = mtod(copym, struct ip6_hdr *); + if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) + ip6->ip6_src.s6_addr16[1] = 0; + if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) + ip6->ip6_dst.s6_addr16[1] = 0; + } + + (void)if_simloop(ifp, copym, dst->sin6_family, NULL); } /* * Chop IPv6 header off from the payload. */ static int ip6_splithdr(m, exthdrs) struct mbuf *m; struct ip6_exthdrs *exthdrs; { struct mbuf *mh; struct ip6_hdr *ip6; ip6 = mtod(m, struct ip6_hdr *); if (m->m_len > sizeof(*ip6)) { MGETHDR(mh, M_DONTWAIT, MT_HEADER); if (mh == 0) { m_freem(m); return ENOBUFS; } M_COPY_PKTHDR(mh, m); MH_ALIGN(mh, sizeof(*ip6)); m->m_flags &= ~M_PKTHDR; m->m_len -= sizeof(*ip6); m->m_data += sizeof(*ip6); mh->m_next = m; m = mh; m->m_len = sizeof(*ip6); bcopy((caddr_t)ip6, mtod(m, caddr_t), sizeof(*ip6)); } exthdrs->ip6e_ip6 = m; return 0; } /* * Compute IPv6 extension header length. */ int ip6_optlen(in6p) struct in6pcb *in6p; { int len; if (!in6p->in6p_outputopts) return 0; len = 0; #define elen(x) \ (((struct ip6_ext *)(x)) ? (((struct ip6_ext *)(x))->ip6e_len + 1) << 3 : 0) len += elen(in6p->in6p_outputopts->ip6po_hbh); len += elen(in6p->in6p_outputopts->ip6po_dest1); len += elen(in6p->in6p_outputopts->ip6po_rthdr); len += elen(in6p->in6p_outputopts->ip6po_dest2); return len; #undef elen } Index: head/sys/netinet6/ip6_var.h =================================================================== --- head/sys/netinet6/ip6_var.h (revision 62586) +++ head/sys/netinet6/ip6_var.h (revision 62587) @@ -1,254 +1,302 @@ +/* $FreeBSD$ */ +/* $KAME: ip6_var.h,v 1.33 2000/06/11 14:59:20 jinmei Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* * Copyright (c) 1982, 1986, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)ip_var.h 8.1 (Berkeley) 6/10/93 */ #ifndef _NETINET6_IP6_VAR_H_ #define _NETINET6_IP6_VAR_H_ /* * IP6 reassembly queue structure. Each fragment * being reassembled is attached to one of these structures. */ struct ip6q { - u_long ip6q_head; - u_short ip6q_len; - u_char ip6q_nxt; - u_char ip6q_hlim; - struct ip6asfrag *ip6q_down; - struct ip6asfrag *ip6q_up; - u_long ip6q_ident; - u_char ip6q_arrive; - u_char ip6q_ttl; - struct in6_addr ip6q_src, ip6q_dst; - struct ip6q *ip6q_next; - struct ip6q *ip6q_prev; - int ip6q_unfrglen; + u_int32_t ip6q_head; + u_int16_t ip6q_len; + u_int8_t ip6q_nxt; /* ip6f_nxt in first fragment */ + u_int8_t ip6q_hlim; + struct ip6asfrag *ip6q_down; + struct ip6asfrag *ip6q_up; + u_int32_t ip6q_ident; + u_int8_t ip6q_arrive; + u_int8_t ip6q_ttl; + struct in6_addr ip6q_src, ip6q_dst; + struct ip6q *ip6q_next; + struct ip6q *ip6q_prev; + int ip6q_unfrglen; /* len of unfragmentable part */ +#ifdef notyet + u_char *ip6q_nxtp; +#endif }; struct ip6asfrag { - u_long ip6af_head; - u_short ip6af_len; - u_char ip6af_nxt; - u_char ip6af_hlim; + u_int32_t ip6af_head; + u_int16_t ip6af_len; + u_int8_t ip6af_nxt; + u_int8_t ip6af_hlim; /* must not override the above members during reassembling */ - struct ip6asfrag *ip6af_down; - struct ip6asfrag *ip6af_up; - u_short ip6af_mff; - u_short ip6af_off; - struct mbuf *ip6af_m; - u_long ip6af_offset; /* offset where next header starts */ - u_short ip6af_frglen; /* fragmentable part length */ - u_char ip6af_x1[10]; + struct ip6asfrag *ip6af_down; + struct ip6asfrag *ip6af_up; + struct mbuf *ip6af_m; + int ip6af_offset; /* offset in ip6af_m to next header */ + int ip6af_frglen; /* fragmentable part length */ + int ip6af_off; /* fragment offset */ + u_int16_t ip6af_mff; /* more fragment bit in frag off */ }; #define IP6_REASS_MBUF(ip6af) (*(struct mbuf **)&((ip6af)->ip6af_m)) struct ip6_moptions { struct ifnet *im6o_multicast_ifp; /* ifp for outgoing multicasts */ u_char im6o_multicast_hlim; /* hoplimit for outgoing multicasts */ u_char im6o_multicast_loop; /* 1 >= hear sends if a member */ LIST_HEAD(, in6_multi_mship) im6o_memberships; }; /* * Control options for outgoing packets */ /* Routing header related info */ struct ip6po_rhinfo { struct ip6_rthdr *ip6po_rhi_rthdr; /* Routing header */ struct route_in6 ip6po_rhi_route; /* Route to the 1st hop */ }; -#define ip6po_rthdr ip6po_rhinfo.ip6po_rhi_rthdr -#define ip6po_route ip6po_rhinfo.ip6po_rhi_route +#define ip6po_rthdr ip6po_rhinfo.ip6po_rhi_rthdr +#define ip6po_route ip6po_rhinfo.ip6po_rhi_route struct ip6_pktopts { struct mbuf *ip6po_m; /* Pointer to mbuf storing the data */ int ip6po_hlim; /* Hoplimit for outgoing packets */ struct in6_pktinfo *ip6po_pktinfo; /* Outgoing IF/address information */ struct sockaddr *ip6po_nexthop; /* Next-hop address */ struct ip6_hbh *ip6po_hbh; /* Hop-by-Hop options header */ struct ip6_dest *ip6po_dest1; /* Destination options header(1st part) */ struct ip6po_rhinfo ip6po_rhinfo; /* Routing header related info. */ struct ip6_dest *ip6po_dest2; /* Destination options header(2nd part) */ }; struct ip6stat { - u_long ip6s_total; /* total packets received */ - u_long ip6s_tooshort; /* packet too short */ - u_long ip6s_toosmall; /* not enough data */ - u_long ip6s_fragments; /* fragments received */ - u_long ip6s_fragdropped; /* frags dropped(dups, out of space) */ - u_long ip6s_fragtimeout; /* fragments timed out */ - u_long ip6s_fragoverflow; /* fragments that exceeded limit */ - u_long ip6s_forward; /* packets forwarded */ - u_long ip6s_cantforward; /* packets rcvd for unreachable dest */ - u_long ip6s_redirectsent; /* packets forwarded on same net */ - u_long ip6s_delivered; /* datagrams delivered to upper level*/ - u_long ip6s_localout; /* total ip packets generated here */ - u_long ip6s_odropped; /* lost packets due to nobufs, etc. */ - u_long ip6s_reassembled; /* total packets reassembled ok */ - u_long ip6s_fragmented; /* datagrams sucessfully fragmented */ - u_long ip6s_ofragments; /* output fragments created */ - u_long ip6s_cantfrag; /* don't fragment flag was set, etc. */ - u_long ip6s_badoptions; /* error in option processing */ - u_long ip6s_noroute; /* packets discarded due to no route */ - u_long ip6s_badvers; /* ip6 version != 6 */ - u_long ip6s_rawout; /* total raw ip packets generated */ - u_long ip6s_badscope; /* scope error */ - u_long ip6s_notmember; /* don't join this multicast group */ - u_long ip6s_nxthist[256]; /* next header history */ - u_long ip6s_m1; /* one mbuf */ - u_long ip6s_m2m[32]; /* two or more mbuf */ - u_long ip6s_mext1; /* one ext mbuf */ - u_long ip6s_mext2m; /* two or more ext mbuf */ - u_long ip6s_exthdrtoolong; /* ext hdr are not continuous */ - u_long ip6s_nogif; /* no match gif found */ - u_long ip6s_toomanyhdr; /* discarded due to too many headers */ + u_quad_t ip6s_total; /* total packets received */ + u_quad_t ip6s_tooshort; /* packet too short */ + u_quad_t ip6s_toosmall; /* not enough data */ + u_quad_t ip6s_fragments; /* fragments received */ + u_quad_t ip6s_fragdropped; /* frags dropped(dups, out of space) */ + u_quad_t ip6s_fragtimeout; /* fragments timed out */ + u_quad_t ip6s_fragoverflow; /* fragments that exceeded limit */ + u_quad_t ip6s_forward; /* packets forwarded */ + u_quad_t ip6s_cantforward; /* packets rcvd for unreachable dest */ + u_quad_t ip6s_redirectsent; /* packets forwarded on same net */ + u_quad_t ip6s_delivered; /* datagrams delivered to upper level*/ + u_quad_t ip6s_localout; /* total ip packets generated here */ + u_quad_t ip6s_odropped; /* lost packets due to nobufs, etc. */ + u_quad_t ip6s_reassembled; /* total packets reassembled ok */ + u_quad_t ip6s_fragmented; /* datagrams sucessfully fragmented */ + u_quad_t ip6s_ofragments; /* output fragments created */ + u_quad_t ip6s_cantfrag; /* don't fragment flag was set, etc. */ + u_quad_t ip6s_badoptions; /* error in option processing */ + u_quad_t ip6s_noroute; /* packets discarded due to no route */ + u_quad_t ip6s_badvers; /* ip6 version != 6 */ + u_quad_t ip6s_rawout; /* total raw ip packets generated */ + u_quad_t ip6s_badscope; /* scope error */ + u_quad_t ip6s_notmember; /* don't join this multicast group */ + u_quad_t ip6s_nxthist[256]; /* next header history */ + u_quad_t ip6s_m1; /* one mbuf */ + u_quad_t ip6s_m2m[32]; /* two or more mbuf */ + u_quad_t ip6s_mext1; /* one ext mbuf */ + u_quad_t ip6s_mext2m; /* two or more ext mbuf */ + u_quad_t ip6s_exthdrtoolong; /* ext hdr are not continuous */ + u_quad_t ip6s_nogif; /* no match gif found */ + u_quad_t ip6s_toomanyhdr; /* discarded due to too many headers */ + /* XXX the following two items are not really AF_INET6 thing */ + u_quad_t ip6s_exthdrget; /* # of calls to IP6_EXTHDR_GET */ + u_quad_t ip6s_exthdrget0; /* # of calls to IP6_EXTHDR_GET0 */ + u_quad_t ip6s_pulldown; /* # of calls to m_pulldown */ + u_quad_t ip6s_pulldown_copy; /* # of mbuf copies in m_pulldown */ + u_quad_t ip6s_pulldown_alloc; /* # of mbuf allocs in m_pulldown */ + u_quad_t ip6s_pullup; /* # of calls to m_pullup */ + u_quad_t ip6s_pullup_copy; /* # of possible m_pullup copies */ + u_quad_t ip6s_pullup_alloc; /* # of possible m_pullup mallocs */ + u_quad_t ip6s_pullup_fail; /* # of possible m_pullup failures */ + u_quad_t ip6s_pullup2; /* # of calls to m_pullup2 */ + u_quad_t ip6s_pullup2_copy; /* # of possible m_pullup2 copies */ + u_quad_t ip6s_pullup2_alloc; /* # of possible m_pullup2 mallocs */ + u_quad_t ip6s_pullup2_fail; /* # of possible m_pullup2 failures */ + + /* + * statistics for improvement of the source address selection + * algorithm: + * XXX: hardcoded 16 = # of ip6 multicast scope types + 1 + */ + /* number of times that address selection fails */ + u_quad_t ip6s_sources_none; + /* number of times that an address on the outgoing I/F is chosen */ + u_quad_t ip6s_sources_sameif[16]; + /* number of times that an address on a non-outgoing I/F is chosen */ + u_quad_t ip6s_sources_otherif[16]; + /* + * number of times that an address that has the same scope + * from the destination is chosen. + */ + u_quad_t ip6s_sources_samescope[16]; + /* + * number of times that an address that has a different scope + * from the destination is chosen. + */ + u_quad_t ip6s_sources_otherscope[16]; + /* number of times that an deprecated address is chosen */ + u_quad_t ip6s_sources_deprecated[16]; + + u_quad_t ip6s_forward_cachehit; + u_quad_t ip6s_forward_cachemiss; }; #ifdef _KERNEL /* flags passed to ip6_output as last parameter */ #define IPV6_DADOUTPUT 0x01 /* DAD */ #define IPV6_FORWARDING 0x02 /* most of IPv6 header exists */ -#define IPV6_SOCKINMRCVIF 0x100 /* IPSEC hack; - * socket pointer in sending - * packet's m_pkthdr.rcvif */ +#define IPV6_MINMTU 0x04 /* use minimum MTU (IPV6_USE_MIN_MTU) */ extern struct ip6stat ip6stat; /* statistics */ -extern u_int32_t ip6_id; /* fragment identifier */ +extern u_int32_t ip6_id; /* fragment identifier */ extern int ip6_defhlim; /* default hop limit */ extern int ip6_defmcasthlim; /* default multicast hop limit */ extern int ip6_forwarding; /* act as router? */ extern int ip6_forward_srcrt; /* forward src-routed? */ extern int ip6_gif_hlim; /* Hop limit for gif encap packet */ extern int ip6_use_deprecated; /* allow deprecated addr as source */ extern int ip6_rr_prune; /* router renumbering prefix * walk list every 5 sec. */ extern int ip6_mapped_addr_on; -extern struct socket *ip6_mrouter; /* multicast routing daemon */ +extern struct socket *ip6_mrouter; /* multicast routing daemon */ extern int ip6_sendredirects; /* send IP redirects when forwarding? */ extern int ip6_maxfragpackets; /* Maximum packets in reassembly queue */ extern int ip6_sourcecheck; /* Verify source interface */ extern int ip6_sourcecheck_interval; /* Interval between log messages */ extern int ip6_accept_rtadv; /* Acts as a host not a router */ extern int ip6_keepfaith; /* Firewall Aided Internet Translator */ extern int ip6_log_interval; extern time_t ip6_log_time; extern int ip6_hdrnestlimit; /* upper limit of # of extension headers */ extern int ip6_dad_count; /* DupAddrDetectionTransmits */ -extern u_int32_t ip6_flow_seq; -extern int ip6_auto_flowlabel; +extern u_int32_t ip6_flow_seq; +extern int ip6_auto_flowlabel; extern struct pr_usrreqs rip6_usrreqs; -struct sockopt; -struct inpcb; +struct sockopt; +struct inpcb; + int icmp6_ctloutput __P((struct socket *, struct sockopt *sopt)); void ip6_init __P((void)); void ip6intr __P((void)); void ip6_input __P((struct mbuf *)); void ip6_freemoptions __P((struct ip6_moptions *)); int ip6_unknown_opt __P((u_int8_t *, struct mbuf *, int)); char * ip6_get_prevhdr __P((struct mbuf *, int)); +int ip6_nexthdr __P((struct mbuf *, int, int, int *)); +int ip6_lasthdr __P((struct mbuf *, int, int, int *)); int ip6_mforward __P((struct ip6_hdr *, struct ifnet *, struct mbuf *)); int ip6_process_hopopts __P((struct mbuf *, u_int8_t *, int, u_int32_t *, u_int32_t *)); void ip6_savecontrol __P((struct inpcb *, struct mbuf **, struct ip6_hdr *, struct mbuf *)); int ip6_sysctl __P((int *, u_int, void *, size_t *, void *, size_t)); void ip6_forward __P((struct mbuf *, int)); void ip6_mloopback __P((struct ifnet *, struct mbuf *, struct sockaddr_in6 *)); int ip6_output __P((struct mbuf *, struct ip6_pktopts *, struct route_in6 *, int, struct ip6_moptions *, struct ifnet **)); int ip6_ctloutput __P((struct socket *, struct sockopt *sopt)); int ip6_setpktoptions __P((struct mbuf *, struct ip6_pktopts *, int)); +void ip6_clearpktopts __P((struct ip6_pktopts *, int, int)); +struct ip6_pktopts *ip6_copypktopts __P((struct ip6_pktopts *, int)); int ip6_optlen __P((struct inpcb *)); int route6_input __P((struct mbuf **, int *, int)); void frag6_init __P((void)); int frag6_input __P((struct mbuf **, int *, int)); void frag6_slowtimo __P((void)); void frag6_drain __P((void)); void rip6_init __P((void)); int rip6_input __P((struct mbuf **mp, int *offp, int proto)); +void rip6_ctlinput __P((int, struct sockaddr *, void *)); int rip6_ctloutput __P((struct socket *so, struct sockopt *sopt)); int rip6_output __P((struct mbuf *, ...)); int rip6_usrreq __P((struct socket *, int, struct mbuf *, struct mbuf *, struct mbuf *, struct proc *)); int dest6_input __P((struct mbuf **, int *, int)); int none_input __P((struct mbuf **, int *, int)); #endif /* _KERNEL */ #endif /* !_NETINET6_IP6_VAR_H_ */ Index: head/sys/netinet6/ip6protosw.h =================================================================== --- head/sys/netinet6/ip6protosw.h (revision 62586) +++ head/sys/netinet6/ip6protosw.h (revision 62587) @@ -1,130 +1,131 @@ +/* $FreeBSD$ */ +/* $KAME: ip6protosw.h,v 1.10 2000/03/29 22:51:25 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD$ */ /* BSDI protosw.h,v 2.3 1996/10/11 16:02:40 pjd Exp */ /*- * Copyright (c) 1982, 1986, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)protosw.h 8.1 (Berkeley) 6/2/93 */ #ifndef _NETINET6_IP6PROTOSW_H_ #define _NETINET6_IP6PROTOSW_H_ /* * Protocol switch table for IPv6. * All other definitions should refer to sys/protosw.h */ -struct mbuf; -struct sockaddr; -struct socket; -struct sockopt; -struct domain; -struct proc; -struct ip6_hdr; -struct pr_usrreqs; +struct mbuf; +struct sockaddr; +struct socket; +struct domain; +struct proc; +struct ip6_hdr; +struct pr_usrreqs; /* * argument type for the last arg of pr_ctlinput(). * should be consulted only with AF_INET6 family. */ struct ip6ctlparam { - struct mbuf *ip6c_m; /* start of mbuf chain */ - struct ip6_hdr *ip6c_ip6; /* ip6 header of target packet */ - int ip6c_off; /* offset of the target proto header */ + struct mbuf *ip6c_m; /* start of mbuf chain */ + struct ip6_hdr *ip6c_ip6; /* ip6 header of target packet */ + int ip6c_off; /* offset of the target proto header */ }; struct ip6protosw { int pr_type; /* socket type used for */ struct domain *pr_domain; /* domain protocol a member of */ short pr_protocol; /* protocol number */ short pr_flags; /* see below */ /* protocol-protocol hooks */ int (*pr_input) /* input to protocol (from below) */ __P((struct mbuf **, int *, int)); int (*pr_output) /* output to protocol (from above) */ __P((struct mbuf *, ...)); void (*pr_ctlinput) /* control input (from below) */ __P((int, struct sockaddr *, void *)); int (*pr_ctloutput) /* control output (from above) */ __P((struct socket *, struct sockopt *)); /* user-protocol hook */ int (*pr_usrreq) /* user request: see list below */ __P((struct socket *, int, struct mbuf *, struct mbuf *, struct mbuf *, struct proc *)); /* utility hooks */ void (*pr_init) /* initialization hook */ __P((void)); void (*pr_fasttimo) /* fast timeout (200ms) */ __P((void)); void (*pr_slowtimo) /* slow timeout (500ms) */ __P((void)); void (*pr_drain) /* flush any excess space possible */ __P((void)); struct pr_usrreqs *pr_usrreqs; /* supersedes pr_usrreq() */ }; #endif /* !_NETINET6_IP6PROTOSW_H_ */ Index: head/sys/netinet6/ipcomp.h =================================================================== --- head/sys/netinet6/ipcomp.h (nonexistent) +++ head/sys/netinet6/ipcomp.h (revision 62587) @@ -0,0 +1,67 @@ +/* $FreeBSD$ */ +/* $KAME: ipcomp.h,v 1.7 2000/05/18 12:45:13 sumikawa Exp $ */ + +/* + * Copyright (C) 1999 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * RFC2393 IP payload compression protocol (IPComp). + */ + +#ifndef _NETINET6_IPCOMP_H_ +#define _NETINET6_IPCOMP_H_ + +struct ipcomp { + u_int8_t comp_nxt; /* Next Header */ + u_int8_t comp_flags; /* reserved, must be zero */ + u_int16_t comp_cpi; /* Compression parameter index */ +}; + +/* well-known algorithm number (in CPI), from RFC2409 */ +#define IPCOMP_OUI 1 /* vendor specific */ +#define IPCOMP_DEFLATE 2 /* RFC2394 */ +#define IPCOMP_LZS 3 /* RFC2395 */ +#define IPCOMP_MAX 4 + +#define IPCOMP_CPI_NEGOTIATE_MIN 256 + +#ifdef _KERNEL +struct ipcomp_algorithm { + int (*compress) __P((struct mbuf *, struct mbuf *, size_t *)); + int (*decompress) __P((struct mbuf *, struct mbuf *, size_t *)); + size_t minplen; /* minimum required length for compression */ +}; + +struct ipsecrequest; +extern struct ipcomp_algorithm ipcomp_algorithms[]; +extern void ipcomp4_input __P((struct mbuf *, ...)); +extern int ipcomp4_output __P((struct mbuf *, struct ipsecrequest *)); +#endif /*KERNEL*/ + +#endif /*_NETINET6_IPCOMP_H_*/ Property changes on: head/sys/netinet6/ipcomp.h ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: head/sys/netinet6/ipcomp6.h =================================================================== --- head/sys/netinet6/ipcomp6.h (nonexistent) +++ head/sys/netinet6/ipcomp6.h (revision 62587) @@ -0,0 +1,46 @@ +/* $FreeBSD$ */ +/* $KAME: ipcomp.h,v 1.7 2000/05/18 12:45:13 sumikawa Exp $ */ + +/* + * Copyright (C) 1999 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * RFC2393 IP payload compression protocol (IPComp). + */ + +#ifndef _NETINET6_IPCOMP6_H_ +#define _NETINET6_IPCOMP6_H_ + +#ifdef _KERNEL +extern int ipcomp6_input __P((struct mbuf **, int *, int)); +extern int ipcomp6_output __P((struct mbuf *, u_char *, struct mbuf *, + struct ipsecrequest *)); +#endif /*KERNEL*/ + +#endif /*_NETINET6_IPCOMP6_H_*/ Property changes on: head/sys/netinet6/ipcomp6.h ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: head/sys/netinet6/ipcomp_core.c =================================================================== --- head/sys/netinet6/ipcomp_core.c (nonexistent) +++ head/sys/netinet6/ipcomp_core.c (revision 62587) @@ -0,0 +1,314 @@ +/* $FreeBSD$ */ +/* $KAME: ipcomp_core.c,v 1.12 2000/05/05 11:01:01 sumikawa Exp $ */ + +/* + * Copyright (C) 1999 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * RFC2393 IP payload compression protocol (IPComp). + */ + +#include "opt_inet.h" +#include "opt_inet6.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#ifdef INET6 +#include +#endif +#include +#ifdef INET6 +#include +#endif + +#include + +#include + +static void *deflate_alloc __P((void *, u_int, u_int)); +static void deflate_free __P((void *, void *)); +static int deflate_common __P((struct mbuf *, struct mbuf *, size_t *, int)); +static int deflate_compress __P((struct mbuf *, struct mbuf *, size_t *)); +static int deflate_decompress __P((struct mbuf *, struct mbuf *, size_t *)); + +/* + * We need to use default window size (2^15 = 32Kbytes as of writing) for + * inbound case. Otherwise we get interop problem. + * Use negative value to avoid Adler32 checksum. This is an undocumented + * feature in zlib (see ipsec wg mailing list archive in January 2000). + */ +static int deflate_policy = Z_DEFAULT_COMPRESSION; +static int deflate_window_out = -12; +static const int deflate_window_in = -1 * MAX_WBITS; /* don't change it */ +static int deflate_memlevel = MAX_MEM_LEVEL; + +struct ipcomp_algorithm ipcomp_algorithms[] = { + { NULL, NULL, -1 }, + { NULL, NULL, -1 }, + { deflate_compress, deflate_decompress, 90 }, + { NULL, NULL, 90 }, +}; + +static void * +deflate_alloc(aux, items, siz) + void *aux; + u_int items; + u_int siz; +{ + void *ptr; + MALLOC(ptr, void *, items * siz, M_TEMP, M_NOWAIT); + return ptr; +} + +static void +deflate_free(aux, ptr) + void *aux; + void *ptr; +{ + FREE(ptr, M_TEMP); +} + +static int +deflate_common(m, md, lenp, mode) + struct mbuf *m; + struct mbuf *md; + size_t *lenp; + int mode; /* 0: compress 1: decompress */ +{ + struct mbuf *mprev; + struct mbuf *p; + struct mbuf *n, *n0 = NULL, **np; + z_stream zs; + int error = 0; + int zerror; + size_t offset; + int firsttime, final, flush; + + for (mprev = m; mprev && mprev->m_next != md; mprev = mprev->m_next) + ; + if (!mprev) + panic("md is not in m in deflate_common"); + + bzero(&zs, sizeof(zs)); + zs.zalloc = deflate_alloc; + zs.zfree = deflate_free; + + zerror = mode ? inflateInit2(&zs, deflate_window_in) + : deflateInit2(&zs, deflate_policy, Z_DEFLATED, + deflate_window_out, deflate_memlevel, + Z_DEFAULT_STRATEGY); + if (zerror != Z_OK) { + error = ENOBUFS; + goto fail; + } + + n0 = n = NULL; + np = &n0; + offset = 0; + firsttime = 1; + final = 0; + flush = Z_NO_FLUSH; + zerror = 0; + p = md; + while (1) { + /* + * first time, we need to setup the buffer before calling + * compression function. + */ + if (firsttime) + firsttime = 0; + else { + zerror = mode ? inflate(&zs, flush) + : deflate(&zs, flush); + } + + /* get input buffer */ + if (p && zs.avail_in == 0) { + zs.next_in = mtod(p, u_int8_t *); + zs.avail_in = p->m_len; + p = p->m_next; + if (!p) { + final = 1; + flush = Z_PARTIAL_FLUSH; + } + } + + /* get output buffer */ + if (zs.next_out == NULL || zs.avail_out == 0) { + /* keep the reply buffer into our chain */ + if (n) { + n->m_len = zs.total_out - offset; + offset = zs.total_out; + *np = n; + np = &n->m_next; + } + + /* get a fresh reply buffer */ + MGET(n, M_DONTWAIT, MT_DATA); + if (n) { + MCLGET(n, M_DONTWAIT); + } + if (!n) { + error = ENOBUFS; + goto fail; + } + n->m_len = 0; + n->m_len = M_TRAILINGSPACE(n); + n->m_next = NULL; + /* + * if this is the first reply buffer, reserve + * region for ipcomp header. + */ + if (*np == NULL) { + n->m_len -= sizeof(struct ipcomp); + n->m_data += sizeof(struct ipcomp); + } + + zs.next_out = mtod(n, u_int8_t *); + zs.avail_out = n->m_len; + } + + if (zerror == Z_OK) { + /* + * to terminate deflate/inflate process, we need to + * call {in,de}flate() with different flushing methods. + * + * deflate() needs at least one Z_PARTIAL_FLUSH, + * then use Z_FINISH until we get to the end. + * (if we use Z_FLUSH without Z_PARTIAL_FLUSH, deflate() + * will assume contiguous single output buffer, and that + * is not what we want) + * inflate() does not care about flushing method, but + * needs output buffer until it gets to the end. + * + * the most outer loop will be terminated with + * Z_STREAM_END. + */ + if (final == 1) { + /* reached end of mbuf chain */ + if (mode == 0) + final = 2; + else + final = 3; + } else if (final == 2) { + /* terminate deflate case */ + flush = Z_FINISH; + } else if (final == 3) { + /* terminate inflate case */ + ; + } + } else if (zerror == Z_STREAM_END) + break; + else { + ipseclog((LOG_ERR, "ipcomp_%scompress: %sflate: %s\n", + mode ? "de" : "", mode ? "in" : "de", + zs.msg ? zs.msg : "unknown error")); + error = EINVAL; + goto fail; + } + } + zerror = mode ? inflateEnd(&zs) : deflateEnd(&zs); + if (zerror != Z_OK) { + ipseclog((LOG_ERR, "ipcomp_%scompress: %sflate: %s\n", + mode ? "de" : "", mode ? "in" : "de", + zs.msg ? zs.msg : "unknown error")); + error = EINVAL; + goto fail; + } + /* keep the final reply buffer into our chain */ + if (n) { + n->m_len = zs.total_out - offset; + offset = zs.total_out; + *np = n; + np = &n->m_next; + } + + /* switch the mbuf to the new one */ + mprev->m_next = n0; + m_freem(md); + *lenp = zs.total_out; + + return 0; + +fail: + if (m) + m_freem(m); + if (n0) + m_freem(n0); + return error; +} + +static int +deflate_compress(m, md, lenp) + struct mbuf *m; + struct mbuf *md; + size_t *lenp; +{ + if (!m) + panic("m == NULL in deflate_compress"); + if (!md) + panic("md == NULL in deflate_compress"); + if (!lenp) + panic("lenp == NULL in deflate_compress"); + + return deflate_common(m, md, lenp, 0); +} + +static int +deflate_decompress(m, md, lenp) + struct mbuf *m; + struct mbuf *md; + size_t *lenp; +{ + if (!m) + panic("m == NULL in deflate_decompress"); + if (!md) + panic("md == NULL in deflate_decompress"); + if (!lenp) + panic("lenp == NULL in deflate_decompress"); + + return deflate_common(m, md, lenp, 1); +} Property changes on: head/sys/netinet6/ipcomp_core.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: head/sys/netinet6/ipcomp_input.c =================================================================== --- head/sys/netinet6/ipcomp_input.c (nonexistent) +++ head/sys/netinet6/ipcomp_input.c (revision 62587) @@ -0,0 +1,407 @@ +/* $FreeBSD$ */ +/* $KAME: ipcomp_input.c,v 1.15 2000/07/03 13:23:28 itojun Exp $ */ + +/* + * Copyright (C) 1999 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * RFC2393 IP payload compression protocol (IPComp). + */ + +#include "opt_inet.h" +#include "opt_inet6.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifdef INET6 +#include +#include +#endif +#include +#ifdef INET6 +#include +#endif + +#include +#ifdef INET6 +#include +#endif +#include +#include + +#include + +#include + +#define IPLEN_FLIPPED + +#ifdef INET +#include +extern struct ipprotosw inetsw[]; + +void +#if __STDC__ +ipcomp4_input(struct mbuf *m, ...) +#else +ipcomp4_input(m, va_alist) + struct mbuf *m; + va_dcl +#endif +{ + struct ip *ip; + struct ipcomp *ipcomp; + struct ipcomp_algorithm *algo; + u_int16_t cpi; /* host order */ + u_int16_t nxt; + size_t hlen; + int error; + size_t newlen, olen; + struct secasvar *sav = NULL; + int off, proto; + va_list ap; + + va_start(ap, m); + off = va_arg(ap, int); + proto = va_arg(ap, int); + va_end(ap); + + if (off + sizeof(struct ipcomp) > MHLEN) { + /*XXX the restriction should be relaxed*/ + ipseclog((LOG_DEBUG, "IPv4 IPComp input: assumption failed " + "(header too long)\n")); + ipsecstat.in_inval++; + goto fail; + } + if (m->m_len < off + sizeof(struct ipcomp)) { + m = m_pullup(m, off + sizeof(struct ipcomp)); + if (!m) { + ipseclog((LOG_DEBUG, "IPv4 IPComp input: can't pullup;" + "dropping the packet for simplicity\n")); + ipsecstat.in_nomem++; + goto fail; + } + } else if (m->m_len > off + sizeof(struct ipcomp)) { + /* chop header part from the packet header chain */ + struct mbuf *n; + MGETHDR(n, M_DONTWAIT, MT_HEADER); + if (!n) { + ipsecstat.in_nomem++; + goto fail; + } + M_COPY_PKTHDR(n, m); + MH_ALIGN(n, off + sizeof(struct ipcomp)); + n->m_len = off + sizeof(struct ipcomp); + bcopy(mtod(m, caddr_t), mtod(n, caddr_t), + off + sizeof(struct ipcomp)); + m_adj(m, off + sizeof(struct ipcomp)); + m->m_flags &= ~M_PKTHDR; + n->m_next = m; + m = n; + } + + ip = mtod(m, struct ip *); + ipcomp = (struct ipcomp *)(((caddr_t)ip) + off); + nxt = ipcomp->comp_nxt; +#ifdef _IP_VHL + hlen = IP_VHL_HL(ip->ip_vhl) << 2; +#else + hlen = ip->ip_hl << 2; +#endif + + cpi = ntohs(ipcomp->comp_cpi); + + if (cpi >= IPCOMP_CPI_NEGOTIATE_MIN) { + sav = key_allocsa(AF_INET, (caddr_t)&ip->ip_src, + (caddr_t)&ip->ip_dst, IPPROTO_IPCOMP, htonl(cpi)); + if (sav != NULL + && (sav->state == SADB_SASTATE_MATURE + || sav->state == SADB_SASTATE_DYING)) { + cpi = sav->alg_enc; /*XXX*/ + /* other parameters to look at? */ + } + } + if (cpi < IPCOMP_MAX && ipcomp_algorithms[cpi].decompress != NULL) + algo = &ipcomp_algorithms[cpi]; + else + algo = NULL; + if (!algo) { + ipseclog((LOG_WARNING, "IPv4 IPComp input: unknown cpi %u\n", + cpi)); + ipsecstat.in_nosa++; + goto fail; + } + + /* chop ipcomp header */ + ipcomp = NULL; + m->m_len -= sizeof(struct ipcomp); + m->m_pkthdr.len -= sizeof(struct ipcomp); +#ifdef IPLEN_FLIPPED + ip->ip_len -= sizeof(struct ipcomp); +#else + ip->ip_len = htons(ntohs(ip->ip_len) - sizeof(struct ipcomp)); +#endif + + olen = m->m_pkthdr.len; + newlen = m->m_pkthdr.len - off; + error = (*algo->decompress)(m, m->m_next, &newlen); + if (error != 0) { + if (error == EINVAL) + ipsecstat.in_inval++; + else if (error == ENOBUFS) + ipsecstat.in_nomem++; + m = NULL; + goto fail; + } + ipsecstat.in_comphist[cpi]++; + + /* + * returning decompressed packet onto icmp is meaningless. + * mark it decrypted to prevent icmp from attaching original packet. + */ + m->m_flags |= M_DECRYPTED; + + m->m_pkthdr.len = off + newlen; + ip = mtod(m, struct ip *); + { + size_t len; +#ifdef IPLEN_FLIPPED + len = ip->ip_len; +#else + len = ntohs(ip->ip_len); +#endif + /* + * be careful about underflow. also, do not assign exact value + * as ip_len is manipulated differently on *BSDs. + */ + len += m->m_pkthdr.len; + len -= olen; + if (len & ~0xffff) { + /* packet too big after decompress */ + ipsecstat.in_inval++; + goto fail; + } +#ifdef IPLEN_FLIPPED + ip->ip_len = len & 0xffff; +#else + ip->ip_len = htons(len & 0xffff); +#endif + ip->ip_p = nxt; + } + + if (sav) { + key_sa_recordxfer(sav, m); + key_freesav(sav); + sav = NULL; + } + + if (nxt != IPPROTO_DONE) + (*inetsw[ip_protox[nxt]].pr_input)(m, off, nxt); + else + m_freem(m); + m = NULL; + + ipsecstat.in_success++; + return; + +fail: + if (sav) + key_freesav(sav); + if (m) + m_freem(m); + return; +} +#endif /* INET */ + +#ifdef INET6 +int +ipcomp6_input(mp, offp, proto) + struct mbuf **mp; + int *offp, proto; +{ + struct mbuf *m, *md; + int off; + struct ip6_hdr *ip6; + struct mbuf *ipcompm; + struct ipcomp *ipcomp; + struct ipcomp_algorithm *algo; + u_int16_t cpi; /* host order */ + u_int16_t nxt; + int error; + size_t newlen; + struct secasvar *sav = NULL; + + m = *mp; + off = *offp; + + IP6_EXTHDR_CHECK(m, off, sizeof(struct ipcomp), IPPROTO_DONE); + + { + int skip; + struct mbuf *n; + struct mbuf *p, *q; + size_t l; + + skip = off; + for (n = m; n && skip > 0; n = n->m_next) { + if (n->m_len <= skip) { + skip -= n->m_len; + continue; + } + break; + } + if (!n) { + ipseclog((LOG_DEBUG, "IPv6 IPComp input: wrong mbuf chain\n")); + ipsecstat.in_inval++; + goto fail; + } + if (n->m_len < skip + sizeof(struct ipcomp)) { + ipseclog((LOG_DEBUG, "IPv6 IPComp input: wrong mbuf chain\n")); + ipsecstat.in_inval++; + goto fail; + } + ip6 = mtod(m, struct ip6_hdr *); + ipcompm = n; + ipcomp = (struct ipcomp *)(mtod(n, caddr_t) + skip); + if (n->m_len > skip + sizeof(struct ipcomp)) { + /* split mbuf to ease the following steps*/ + l = n->m_len - (skip + sizeof(struct ipcomp)); + p = m_copym(n, skip + sizeof(struct ipcomp), l , M_DONTWAIT); + if (!p) { + ipsecstat.in_nomem++; + goto fail; + } + for (q = p; q && q->m_next; q = q->m_next) + ; + q->m_next = n->m_next; + n->m_next = p; + n->m_len -= l; + md = p; + } else + md = n->m_next; + } + + nxt = ipcomp->comp_nxt; + cpi = ntohs(ipcomp->comp_cpi); + + if (cpi >= IPCOMP_CPI_NEGOTIATE_MIN) { + sav = key_allocsa(AF_INET6, (caddr_t)&ip6->ip6_src, + (caddr_t)&ip6->ip6_dst, IPPROTO_IPCOMP, htonl(cpi)); + if (sav != NULL + && (sav->state == SADB_SASTATE_MATURE + || sav->state == SADB_SASTATE_DYING)) { + cpi = sav->alg_enc; /*XXX*/ + /* other parameters to look at? */ + } + } + if (cpi < IPCOMP_MAX && ipcomp_algorithms[cpi].decompress != NULL) + algo = &ipcomp_algorithms[cpi]; + else + algo = NULL; + if (!algo) { + ipseclog((LOG_WARNING, "IPv6 IPComp input: unknown cpi %u; " + "dropping the packet for simplicity\n", cpi)); + ipsec6stat.in_nosa++; + goto fail; + } + + newlen = m->m_pkthdr.len - off - sizeof(struct ipcomp); + error = (*algo->decompress)(m, md, &newlen); + if (error != 0) { + if (error == EINVAL) + ipsec6stat.in_inval++; + else if (error == ENOBUFS) + ipsec6stat.in_nomem++; + m = NULL; + goto fail; + } + ipsec6stat.in_comphist[cpi]++; + m->m_pkthdr.len = off + sizeof(struct ipcomp) + newlen; + + /* + * returning decompressed packet onto icmp is meaningless. + * mark it decrypted to prevent icmp from attaching original packet. + */ + m->m_flags |= M_DECRYPTED; + + { + char *prvnxtp; + + /* chop IPComp header */ + prvnxtp = ip6_get_prevhdr(m, off); + *prvnxtp = nxt; + ipcompm->m_len -= sizeof(struct ipcomp); + ipcompm->m_pkthdr.len -= sizeof(struct ipcomp); + + /* adjust payload length */ + ip6 = mtod(m, struct ip6_hdr *); + if (((m->m_pkthdr.len - sizeof(struct ip6_hdr)) & ~0xffff) != 0) + ip6->ip6_plen = 0; /*now a jumbogram*/ + else + ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(struct ip6_hdr)); + } + + if (sav) { + key_sa_recordxfer(sav, m); + key_freesav(sav); + sav = NULL; + } + *offp = off; + *mp = m; + ipsec6stat.in_success++; + return nxt; + +fail: + if (m) + m_freem(m); + if (sav) + key_freesav(sav); + return IPPROTO_DONE; +} +#endif /* INET6 */ Property changes on: head/sys/netinet6/ipcomp_input.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: head/sys/netinet6/ipcomp_output.c =================================================================== --- head/sys/netinet6/ipcomp_output.c (nonexistent) +++ head/sys/netinet6/ipcomp_output.c (revision 62587) @@ -0,0 +1,430 @@ +/* $FreeBSD$ */ +/* $KAME: ipcomp_output.c,v 1.15 2000/07/03 13:23:28 itojun Exp $ */ + +/* + * Copyright (C) 1999 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * RFC2393 IP payload compression protocol (IPComp). + */ + +#include "opt_inet.h" +#include "opt_inet6.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifdef INET6 +#include +#include +#endif +#include +#ifdef INET6 +#include +#endif + +#include +#ifdef INET6 +#include +#endif +#include +#include + +#include + +#include + +static int ipcomp_output __P((struct mbuf *, u_char *, struct mbuf *, + struct ipsecrequest *, int)); + +/* + * Modify the packet so that the payload is compressed. + * The mbuf (m) must start with IPv4 or IPv6 header. + * On failure, free the given mbuf and return NULL. + * + * on invocation: + * m nexthdrp md + * v v v + * IP ......... payload + * during the encryption: + * m nexthdrp mprev md + * v v v v + * IP ............... ipcomp payload + * <-----><-----> + * complen plen + * <-> hlen + * <-----------------> compoff + */ +static int +ipcomp_output(m, nexthdrp, md, isr, af) + struct mbuf *m; + u_char *nexthdrp; + struct mbuf *md; + struct ipsecrequest *isr; + int af; +{ + struct mbuf *n; + struct mbuf *md0; + struct mbuf *mprev; + struct ipcomp *ipcomp; + struct secasvar *sav = isr->sav; + struct ipcomp_algorithm *algo; + u_int16_t cpi; /* host order */ + size_t plen0, plen; /*payload length to be compressed*/ + size_t compoff; + int afnumber; + int error = 0; + + switch (af) { +#ifdef INET + case AF_INET: + afnumber = 4; + break; +#endif +#ifdef INET6 + case AF_INET6: + afnumber = 6; + break; +#endif + default: + ipseclog((LOG_ERR, "ipcomp_output: unsupported af %d\n", af)); + return 0; /* no change at all */ + } + + /* grab parameters */ + if ((ntohl(sav->spi) & ~0xffff) != 0 || sav->alg_enc >= IPCOMP_MAX + || ipcomp_algorithms[sav->alg_enc].compress == NULL) { + ipsecstat.out_inval++; + m_freem(m); + return EINVAL; + } + if ((sav->flags & SADB_X_EXT_RAWCPI) == 0) + cpi = sav->alg_enc; + else + cpi = ntohl(sav->spi) & 0xffff; + algo = &ipcomp_algorithms[sav->alg_enc]; /*XXX*/ + + /* compute original payload length */ + plen = 0; + for (n = md; n; n = n->m_next) + plen += n->m_len; + + /* if the payload is short enough, we don't need to compress */ + if (plen < algo->minplen) + return 0; + + /* + * keep the original data packet, so that we can backout + * our changes when compression is not necessary. + */ + md0 = m_copym(md, 0, M_COPYALL, M_NOWAIT); + if (md0 == NULL) { + error = ENOBUFS; + return 0; + } + plen0 = plen; + + /* make the packet over-writable */ + for (mprev = m; mprev && mprev->m_next != md; mprev = mprev->m_next) + ; + if (mprev == NULL || mprev->m_next != md) { + ipseclog((LOG_DEBUG, "ipcomp%d_output: md is not in chain\n", + afnumber)); + switch (af) { +#ifdef INET + case AF_INET: + ipsecstat.out_inval++; + break; +#endif +#ifdef INET6 + case AF_INET6: + ipsec6stat.out_inval++; + break; +#endif + } + m_freem(m); + m_freem(md0); + return EINVAL; + } + mprev->m_next = NULL; + if ((md = ipsec_copypkt(md)) == NULL) { + m_freem(m); + m_freem(md0); + error = ENOBUFS; + goto fail; + } + mprev->m_next = md; + + /* compress data part */ + if ((*algo->compress)(m, md, &plen) || mprev->m_next == NULL) { + ipseclog((LOG_ERR, "packet compression failure\n")); + m = NULL; + m_freem(md0); + switch (af) { +#ifdef INET + case AF_INET: + ipsecstat.out_inval++; + break; +#endif +#ifdef INET6 + case AF_INET6: + ipsec6stat.out_inval++; + break; +#endif + } + error = EINVAL; + goto fail; + } + switch (af) { +#ifdef INET + case AF_INET: + ipsecstat.out_comphist[sav->alg_enc]++; + break; +#endif +#ifdef INET6 + case AF_INET6: + ipsec6stat.out_comphist[sav->alg_enc]++; + break; +#endif + } + md = mprev->m_next; + + /* + * if the packet became bigger, meaningless to use IPComp. + * we've only wasted our cpu time. + */ + if (plen0 < plen) { + m_freem(md); + mprev->m_next = md0; + return 0; + } + + /* no need to backout change beyond here */ + m_freem(md0); + md0 = NULL; + m->m_pkthdr.len -= plen0; + m->m_pkthdr.len += plen; + + { + /* + * insert IPComp header. + */ +#ifdef INET + struct ip *ip = NULL; +#endif +#ifdef INET6 + struct ip6_hdr *ip6 = NULL; +#endif + size_t hlen = 0; /*ip header len*/ + size_t complen = sizeof(struct ipcomp); + + switch (af) { +#ifdef INET + case AF_INET: + ip = mtod(m, struct ip *); +#ifdef _IP_VHL + hlen = IP_VHL_HL(ip->ip_vhl) << 2; +#else + hlen = ip->ip_hl << 2; +#endif + break; +#endif +#ifdef INET6 + case AF_INET6: + ip6 = mtod(m, struct ip6_hdr *); + hlen = sizeof(*ip6); + break; +#endif + } + + compoff = m->m_pkthdr.len - plen; + + /* + * grow the mbuf to accomodate ipcomp header. + * before: IP ... payload + * after: IP ... ipcomp payload + */ + if (M_LEADINGSPACE(md) < complen) { + MGET(n, M_DONTWAIT, MT_DATA); + if (!n) { + m_freem(m); + error = ENOBUFS; + goto fail; + } + n->m_len = complen; + mprev->m_next = n; + n->m_next = md; + m->m_pkthdr.len += complen; + ipcomp = mtod(n, struct ipcomp *); + } else { + md->m_len += complen; + md->m_data -= complen; + m->m_pkthdr.len += complen; + ipcomp = mtod(md, struct ipcomp *); + } + + bzero(ipcomp, sizeof(*ipcomp)); + ipcomp->comp_nxt = *nexthdrp; + *nexthdrp = IPPROTO_IPCOMP; + ipcomp->comp_cpi = htons(cpi); + switch (af) { +#ifdef INET + case AF_INET: + if (compoff + complen + plen < IP_MAXPACKET) + ip->ip_len = htons(compoff + complen + plen); + else { + ipseclog((LOG_ERR, + "IPv4 ESP output: size exceeds limit\n")); + ipsecstat.out_inval++; + m_freem(m); + error = EMSGSIZE; + goto fail; + } + break; +#endif +#ifdef INET6 + case AF_INET6: + /* total packet length will be computed in ip6_output() */ + break; +#endif + } + } + + if (!m) { + ipseclog((LOG_DEBUG, + "NULL mbuf after compression in ipcomp%d_output", + afnumber)); + switch (af) { +#ifdef INET + case AF_INET: + ipsecstat.out_inval++; + break; +#endif +#ifdef INET6 + case AF_INET6: + ipsec6stat.out_inval++; + break; +#endif + } + } else { + switch (af) { +#ifdef INET + case AF_INET: + ipsecstat.out_success++; + break; +#endif +#ifdef INET6 + case AF_INET6: + ipsec6stat.out_success++; + break; +#endif + } + } +#if 0 + switch (af) { +#ifdef INET + case AF_INET: + ipsecstat.out_esphist[sav->alg_enc]++; + break; +#endif +#ifdef INET6 + case AF_INET6: + ipsec6stat.out_esphist[sav->alg_enc]++; + break; +#endif + } +#endif + key_sa_recordxfer(sav, m); + return 0; + +fail: +#if 1 + return error; +#else + panic("something bad in ipcomp_output"); +#endif +} + +#ifdef INET +int +ipcomp4_output(m, isr) + struct mbuf *m; + struct ipsecrequest *isr; +{ + struct ip *ip; + if (m->m_len < sizeof(struct ip)) { + ipseclog((LOG_DEBUG, "ipcomp4_output: first mbuf too short\n")); + ipsecstat.out_inval++; + m_freem(m); + return 0; + } + ip = mtod(m, struct ip *); + /* XXX assumes that m->m_next points to payload */ + return ipcomp_output(m, &ip->ip_p, m->m_next, isr, AF_INET); +} +#endif /*INET*/ + +#ifdef INET6 +int +ipcomp6_output(m, nexthdrp, md, isr) + struct mbuf *m; + u_char *nexthdrp; + struct mbuf *md; + struct ipsecrequest *isr; +{ + if (m->m_len < sizeof(struct ip6_hdr)) { + ipseclog((LOG_DEBUG, "ipcomp6_output: first mbuf too short\n")); + ipsec6stat.out_inval++; + m_freem(m); + return 0; + } + return ipcomp_output(m, nexthdrp, md, isr, AF_INET6); +} +#endif /*INET6*/ Property changes on: head/sys/netinet6/ipcomp_output.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: head/sys/netinet6/ipsec.c =================================================================== --- head/sys/netinet6/ipsec.c (revision 62586) +++ head/sys/netinet6/ipsec.c (revision 62587) @@ -1,3061 +1,3354 @@ +/* $FreeBSD$ */ +/* $KAME: ipsec.c,v 1.66 2000/06/15 04:08:54 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* * IPsec controller part. */ #include "opt_inet.h" #include "opt_inet6.h" #include "opt_ipsec.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include #include #include #include #include #ifdef INET6 #include #endif #ifdef INET6 -#include -#include +#include #include -#include -#endif /*INET6*/ +#endif +#include +#ifdef INET6 +#include +#endif #include -#include #ifdef INET6 #include +#endif +#include +#ifdef INET6 #include #endif #ifdef IPSEC_ESP #include #ifdef INET6 #include #endif #endif - +#include +#ifdef INET6 +#include +#endif #include -#include #include #ifdef IPSEC_DEBUG #include #else #define KEYDEBUG(lev,arg) #endif #include +#include + +#ifdef HAVE_NRL_INPCB +#define in6pcb inpcb +#define in6p_sp inp_sp +#define in6p_fport inp_fport +#define in6p_lport inp_lport +#define in6p_socket inp_socket +#define sotoin6pcb(so) ((struct inpcb *)(so)->so_pcb) +#endif + +#ifdef IPSEC_DEBUG +int ipsec_debug = 1; +#else +int ipsec_debug = 0; +#endif + struct ipsecstat ipsecstat; -int ip4_inbound_call_ike = 0; int ip4_ah_cleartos = 1; int ip4_ah_offsetmask = 0; /* maybe IP_DF? */ int ip4_ipsec_dfbit = 0; /* DF bit on encap. 0: clear 1: set 2: copy */ int ip4_esp_trans_deflev = IPSEC_LEVEL_USE; int ip4_esp_net_deflev = IPSEC_LEVEL_USE; int ip4_ah_trans_deflev = IPSEC_LEVEL_USE; int ip4_ah_net_deflev = IPSEC_LEVEL_USE; struct secpolicy ip4_def_policy; int ip4_ipsec_ecn = 0; /* ECN ignore(-1)/forbidden(0)/allowed(1) */ +SYSCTL_DECL(_net_inet_ipsec); +#ifdef INET6 +SYSCTL_DECL(_net_inet6_ipsec6); +#endif + /* net.inet.ipsec */ SYSCTL_STRUCT(_net_inet_ipsec, IPSECCTL_STATS, stats, CTLFLAG_RD, &ipsecstat, ipsecstat, ""); SYSCTL_INT(_net_inet_ipsec, IPSECCTL_DEF_POLICY, def_policy, CTLFLAG_RW, &ip4_def_policy.policy, 0, ""); SYSCTL_INT(_net_inet_ipsec, IPSECCTL_DEF_ESP_TRANSLEV, esp_trans_deflev, CTLFLAG_RW, &ip4_esp_trans_deflev, 0, ""); SYSCTL_INT(_net_inet_ipsec, IPSECCTL_DEF_ESP_NETLEV, esp_net_deflev, CTLFLAG_RW, &ip4_esp_net_deflev, 0, ""); SYSCTL_INT(_net_inet_ipsec, IPSECCTL_DEF_AH_TRANSLEV, ah_trans_deflev, CTLFLAG_RW, &ip4_ah_trans_deflev, 0, ""); SYSCTL_INT(_net_inet_ipsec, IPSECCTL_DEF_AH_NETLEV, ah_net_deflev, CTLFLAG_RW, &ip4_ah_net_deflev, 0, ""); -SYSCTL_INT(_net_inet_ipsec, IPSECCTL_INBOUND_CALL_IKE, - inbound_call_ike, CTLFLAG_RW, &ip4_inbound_call_ike, 0, ""); SYSCTL_INT(_net_inet_ipsec, IPSECCTL_AH_CLEARTOS, ah_cleartos, CTLFLAG_RW, &ip4_ah_cleartos, 0, ""); SYSCTL_INT(_net_inet_ipsec, IPSECCTL_AH_OFFSETMASK, ah_offsetmask, CTLFLAG_RW, &ip4_ah_offsetmask, 0, ""); SYSCTL_INT(_net_inet_ipsec, IPSECCTL_DFBIT, dfbit, CTLFLAG_RW, &ip4_ipsec_dfbit, 0, ""); SYSCTL_INT(_net_inet_ipsec, IPSECCTL_ECN, ecn, CTLFLAG_RW, &ip4_ipsec_ecn, 0, ""); +SYSCTL_INT(_net_inet_ipsec, IPSECCTL_DEBUG, + debug, CTLFLAG_RW, &ipsec_debug, 0, ""); #ifdef INET6 struct ipsecstat ipsec6stat; -int ip6_inbound_call_ike = 0; int ip6_esp_trans_deflev = IPSEC_LEVEL_USE; int ip6_esp_net_deflev = IPSEC_LEVEL_USE; int ip6_ah_trans_deflev = IPSEC_LEVEL_USE; int ip6_ah_net_deflev = IPSEC_LEVEL_USE; struct secpolicy ip6_def_policy; int ip6_ipsec_ecn = 0; /* ECN ignore(-1)/forbidden(0)/allowed(1) */ /* net.inet6.ipsec6 */ SYSCTL_STRUCT(_net_inet6_ipsec6, IPSECCTL_STATS, stats, CTLFLAG_RD, &ipsec6stat, ipsecstat, ""); SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_DEF_POLICY, def_policy, CTLFLAG_RW, &ip6_def_policy.policy, 0, ""); SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_DEF_ESP_TRANSLEV, esp_trans_deflev, CTLFLAG_RW, &ip6_esp_trans_deflev, 0, ""); SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_DEF_ESP_NETLEV, esp_net_deflev, CTLFLAG_RW, &ip6_esp_net_deflev, 0, ""); SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_DEF_AH_TRANSLEV, ah_trans_deflev, CTLFLAG_RW, &ip6_ah_trans_deflev, 0, ""); SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_DEF_AH_NETLEV, ah_net_deflev, CTLFLAG_RW, &ip6_ah_net_deflev, 0, ""); -SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_INBOUND_CALL_IKE, - inbound_call_ike, CTLFLAG_RW, &ip6_inbound_call_ike, 0, ""); SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_ECN, ecn, CTLFLAG_RW, &ip6_ipsec_ecn, 0, ""); +SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_DEBUG, + debug, CTLFLAG_RW, &ipsec_debug, 0, ""); #endif /* INET6 */ static int ipsec_setspidx_mbuf __P((struct secpolicyindex *, u_int, u_int, struct mbuf *)); static void ipsec4_setspidx_inpcb __P((struct mbuf *, struct inpcb *pcb)); static void ipsec4_setspidx_ipaddr __P((struct mbuf *, struct secpolicyindex *)); #ifdef INET6 -static u_int16_t ipsec6_get_ulp __P((struct mbuf *m)); +static void ipsec6_get_ulp __P((struct mbuf *m, struct secpolicyindex *)); static void ipsec6_setspidx_in6pcb __P((struct mbuf *, struct in6pcb *pcb)); static void ipsec6_setspidx_ipaddr __P((struct mbuf *, struct secpolicyindex *)); #endif +static struct inpcbpolicy *ipsec_newpcbpolicy __P((void)); +static void ipsec_delpcbpolicy __P((struct inpcbpolicy *)); static struct secpolicy *ipsec_deepcopy_policy __P((struct secpolicy *src)); static int ipsec_set_policy __P((struct secpolicy **pcb_sp, - int optname, caddr_t request, int priv)); + int optname, caddr_t request, size_t len, int priv)); static int ipsec_get_policy __P((struct secpolicy *pcb_sp, struct mbuf **mp)); static void vshiftl __P((unsigned char *, int, int)); static int ipsec_in_reject __P((struct secpolicy *, struct mbuf *)); static size_t ipsec_hdrsiz __P((struct secpolicy *)); static struct mbuf *ipsec4_splithdr __P((struct mbuf *)); #ifdef INET6 static struct mbuf *ipsec6_splithdr __P((struct mbuf *)); #endif static int ipsec4_encapsulate __P((struct mbuf *, struct secasvar *)); #ifdef INET6 static int ipsec6_encapsulate __P((struct mbuf *, struct secasvar *)); #endif -#define KMALLOC(p, t, n) \ - ((p) = (t) malloc((unsigned long)(n), M_SECA, M_NOWAIT)) -#define KFREE(p) \ - free((caddr_t)(p), M_SECA); - /* * For OUTBOUND packet having a socket. Searching SPD for packet, * and return a pointer to SP. * OUT: NULL: no apropreate SP found, the following value is set to error. * 0 : bypass * EACCES : discard packet. * ENOENT : ipsec_acquire() in progress, maybe. * others : error occured. * others: a pointer to SP * * NOTE: IPv6 mapped adddress concern is implemented here. */ struct secpolicy * ipsec4_getpolicybysock(m, dir, so, error) struct mbuf *m; u_int dir; struct socket *so; int *error; { struct inpcbpolicy *pcbsp = NULL; struct secpolicy *currsp = NULL; /* policy on socket */ struct secpolicy *kernsp = NULL; /* policy on kernel */ /* sanity check */ if (m == NULL || so == NULL || error == NULL) panic("ipsec4_getpolicybysock: NULL pointer was passed.\n"); - if ((sotoinpcb(so)->inp_vflag & INP_IPV4) != 0) { + switch (so->so_proto->pr_domain->dom_family) { + case AF_INET: /* set spidx in pcb */ ipsec4_setspidx_inpcb(m, sotoinpcb(so)); pcbsp = sotoinpcb(so)->inp_sp; - } + break; #ifdef INET6 - else if ((sotoinpcb(so)->inp_vflag & INP_IPV6) != 0) { + case AF_INET6: /* set spidx in pcb */ ipsec6_setspidx_in6pcb(m, sotoin6pcb(so)); pcbsp = sotoin6pcb(so)->in6p_sp; - } + break; #endif - else + default: panic("ipsec4_getpolicybysock: unsupported address family\n"); + } /* sanity check */ if (pcbsp == NULL) panic("ipsec4_getpolicybysock: pcbsp is NULL.\n"); - KEYDEBUG(KEYDEBUG_IPSEC_DATA, - printf("send: priv=%d ", pcbsp->priv); - if (so->so_cred) - printf("cr_uid=%d\n", so->so_cred->cr_uid); - ); switch (dir) { case IPSEC_DIR_INBOUND: currsp = pcbsp->sp_in; break; case IPSEC_DIR_OUTBOUND: currsp = pcbsp->sp_out; break; default: panic("ipsec4_getpolicybysock: illegal direction.\n"); } /* sanity check */ if (currsp == NULL) panic("ipsec4_getpolicybysock: currsp is NULL.\n"); /* when privilieged socket */ if (pcbsp->priv) { switch (currsp->policy) { case IPSEC_POLICY_BYPASS: currsp->refcnt++; *error = 0; return currsp; case IPSEC_POLICY_ENTRUST: /* look for a policy in SPD */ kernsp = key_allocsp(&currsp->spidx, dir); /* SP found */ if (kernsp != NULL) { KEYDEBUG(KEYDEBUG_IPSEC_STAMP, printf("DP ipsec4_getpolicybysock called " "to allocate SP:%p\n", kernsp)); *error = 0; return kernsp; } /* no SP found */ if (ip4_def_policy.policy != IPSEC_POLICY_DISCARD && ip4_def_policy.policy != IPSEC_POLICY_NONE) { - printf("fixed system default policy:%d->%d\n", - ip4_def_policy.policy, - IPSEC_POLICY_NONE); + ipseclog((LOG_INFO, + "fixed system default policy: %d->%d\n", + ip4_def_policy.policy, IPSEC_POLICY_NONE)); ip4_def_policy.policy = IPSEC_POLICY_NONE; } ip4_def_policy.refcnt++; *error = 0; return &ip4_def_policy; - + case IPSEC_POLICY_IPSEC: currsp->refcnt++; *error = 0; return currsp; default: - printf("ipsec4_getpolicybysock: " - "Invalid policy for PCB %d\n", - currsp->policy); + ipseclog((LOG_ERR, "ipsec4_getpolicybysock: " + "Invalid policy for PCB %d\n", currsp->policy)); *error = EINVAL; return NULL; } /* NOTREACHED */ } /* when non-privilieged socket */ /* look for a policy in SPD */ kernsp = key_allocsp(&currsp->spidx, dir); /* SP found */ if (kernsp != NULL) { KEYDEBUG(KEYDEBUG_IPSEC_STAMP, printf("DP ipsec4_getpolicybysock called " "to allocate SP:%p\n", kernsp)); *error = 0; return kernsp; } /* no SP found */ switch (currsp->policy) { case IPSEC_POLICY_BYPASS: - printf("ipsec4_getpolicybysock: " + ipseclog((LOG_ERR, "ipsec4_getpolicybysock: " "Illegal policy for non-priviliged defined %d\n", - currsp->policy); + currsp->policy)); *error = EINVAL; return NULL; case IPSEC_POLICY_ENTRUST: if (ip4_def_policy.policy != IPSEC_POLICY_DISCARD && ip4_def_policy.policy != IPSEC_POLICY_NONE) { - printf("fixed system default policy:%d->%d\n", - ip4_def_policy.policy, - IPSEC_POLICY_NONE); + ipseclog((LOG_INFO, + "fixed system default policy: %d->%d\n", + ip4_def_policy.policy, IPSEC_POLICY_NONE)); ip4_def_policy.policy = IPSEC_POLICY_NONE; } ip4_def_policy.refcnt++; *error = 0; return &ip4_def_policy; case IPSEC_POLICY_IPSEC: currsp->refcnt++; *error = 0; return currsp; default: - printf("ipsec4_getpolicybysock: " - "Invalid policy for PCB %d\n", - currsp->policy); + ipseclog((LOG_ERR, "ipsec4_getpolicybysock: " + "Invalid policy for PCB %d\n", currsp->policy)); *error = EINVAL; return NULL; } /* NOTREACHED */ } /* * For FORWADING packet or OUTBOUND without a socket. Searching SPD for packet, * and return a pointer to SP. * OUT: positive: a pointer to the entry for security policy leaf matched. * NULL: no apropreate SP found, the following value is set to error. * 0 : bypass * EACCES : discard packet. * ENOENT : ipsec_acquire() in progress, maybe. * others : error occured. */ struct secpolicy * ipsec4_getpolicybyaddr(m, dir, flag, error) struct mbuf *m; u_int dir; int flag; int *error; { struct secpolicy *sp = NULL; /* sanity check */ if (m == NULL || error == NULL) panic("ipsec4_getpolicybyaddr: NULL pointer was passed.\n"); { struct secpolicyindex spidx; bzero(&spidx, sizeof(spidx)); /* make a index to look for a policy */ - if ((flag & IP_FORWARDING) == IP_FORWARDING) { - /* Case: IP forwarding */ - *error = ipsec_setspidx_mbuf(&spidx, dir, AF_INET, m); - } else { - /* Case: ICMP echo reply */ - *error = ipsec_setspidx_mbuf(&spidx, dir, AF_INET, m); - } + *error = ipsec_setspidx_mbuf(&spidx, dir, AF_INET, m); if (*error != 0) return NULL; sp = key_allocsp(&spidx, dir); } /* SP found */ if (sp != NULL) { KEYDEBUG(KEYDEBUG_IPSEC_STAMP, printf("DP ipsec4_getpolicybyaddr called " "to allocate SP:%p\n", sp)); *error = 0; return sp; } /* no SP found */ if (ip4_def_policy.policy != IPSEC_POLICY_DISCARD && ip4_def_policy.policy != IPSEC_POLICY_NONE) { - printf("fixed system default policy:%d->%d\n", + ipseclog((LOG_INFO, "fixed system default policy:%d->%d\n", ip4_def_policy.policy, - IPSEC_POLICY_NONE); + IPSEC_POLICY_NONE)); ip4_def_policy.policy = IPSEC_POLICY_NONE; } ip4_def_policy.refcnt++; *error = 0; return &ip4_def_policy; } #ifdef INET6 /* * For OUTBOUND packet having a socket. Searching SPD for packet, * and return a pointer to SP. * OUT: NULL: no apropreate SP found, the following value is set to error. * 0 : bypass * EACCES : discard packet. * ENOENT : ipsec_acquire() in progress, maybe. * others : error occured. * others: a pointer to SP */ struct secpolicy * ipsec6_getpolicybysock(m, dir, so, error) struct mbuf *m; u_int dir; struct socket *so; int *error; { struct inpcbpolicy *pcbsp = NULL; struct secpolicy *currsp = NULL; /* policy on socket */ struct secpolicy *kernsp = NULL; /* policy on kernel */ /* sanity check */ if (m == NULL || so == NULL || error == NULL) panic("ipsec6_getpolicybysock: NULL pointer was passed.\n"); /* set spidx in pcb */ ipsec6_setspidx_in6pcb(m, sotoin6pcb(so)); pcbsp = sotoin6pcb(so)->in6p_sp; /* sanity check */ if (pcbsp == NULL) panic("ipsec6_getpolicybysock: pcbsp is NULL.\n"); switch (dir) { case IPSEC_DIR_INBOUND: currsp = pcbsp->sp_in; break; case IPSEC_DIR_OUTBOUND: currsp = pcbsp->sp_out; break; default: panic("ipsec6_getpolicybysock: illegal direction.\n"); } /* sanity check */ if (currsp == NULL) panic("ipsec6_getpolicybysock: currsp is NULL.\n"); /* when privilieged socket */ if (pcbsp->priv) { switch (currsp->policy) { case IPSEC_POLICY_BYPASS: currsp->refcnt++; *error = 0; return currsp; case IPSEC_POLICY_ENTRUST: /* look for a policy in SPD */ kernsp = key_allocsp(&currsp->spidx, dir); /* SP found */ if (kernsp != NULL) { KEYDEBUG(KEYDEBUG_IPSEC_STAMP, printf("DP ipsec6_getpolicybysock called " "to allocate SP:%p\n", kernsp)); *error = 0; return kernsp; } /* no SP found */ if (ip6_def_policy.policy != IPSEC_POLICY_DISCARD && ip6_def_policy.policy != IPSEC_POLICY_NONE) { - printf("fixed system default policy:%d->%d\n", - ip6_def_policy.policy, - IPSEC_POLICY_NONE); + ipseclog((LOG_INFO, + "fixed system default policy: %d->%d\n", + ip6_def_policy.policy, IPSEC_POLICY_NONE)); ip6_def_policy.policy = IPSEC_POLICY_NONE; } ip6_def_policy.refcnt++; *error = 0; return &ip6_def_policy; - + case IPSEC_POLICY_IPSEC: currsp->refcnt++; *error = 0; return currsp; default: - printf("ipsec6_getpolicybysock: " - "Invalid policy for PCB %d\n", - currsp->policy); + ipseclog((LOG_ERR, "ipsec6_getpolicybysock: " + "Invalid policy for PCB %d\n", currsp->policy)); *error = EINVAL; return NULL; } /* NOTREACHED */ } /* when non-privilieged socket */ /* look for a policy in SPD */ kernsp = key_allocsp(&currsp->spidx, dir); /* SP found */ if (kernsp != NULL) { KEYDEBUG(KEYDEBUG_IPSEC_STAMP, printf("DP ipsec6_getpolicybysock called " "to allocate SP:%p\n", kernsp)); *error = 0; return kernsp; } /* no SP found */ switch (currsp->policy) { case IPSEC_POLICY_BYPASS: - printf("ipsec6_getpolicybysock: " - "Illegal policy for non-priviliged defined %d\n", - currsp->policy); + ipseclog((LOG_ERR, "ipsec6_getpolicybysock: " + "Illegal policy for non-priviliged defined %d\n", + currsp->policy)); *error = EINVAL; return NULL; case IPSEC_POLICY_ENTRUST: if (ip6_def_policy.policy != IPSEC_POLICY_DISCARD && ip6_def_policy.policy != IPSEC_POLICY_NONE) { - printf("fixed system default policy:%d->%d\n", - ip6_def_policy.policy, - IPSEC_POLICY_NONE); + ipseclog((LOG_INFO, + "fixed system default policy: %d->%d\n", + ip6_def_policy.policy, IPSEC_POLICY_NONE)); ip6_def_policy.policy = IPSEC_POLICY_NONE; } ip6_def_policy.refcnt++; *error = 0; return &ip6_def_policy; case IPSEC_POLICY_IPSEC: currsp->refcnt++; *error = 0; return currsp; default: - printf("ipsec6_policybysock: " - "Invalid policy for PCB %d\n", - currsp->policy); + ipseclog((LOG_ERR, + "ipsec6_policybysock: Invalid policy for PCB %d\n", + currsp->policy)); *error = EINVAL; return NULL; } /* NOTREACHED */ } /* * For FORWADING packet or OUTBOUND without a socket. Searching SPD for packet, * and return a pointer to SP. * `flag' means that packet is to be forwarded whether or not. * flag = 1: forwad * OUT: positive: a pointer to the entry for security policy leaf matched. * NULL: no apropreate SP found, the following value is set to error. * 0 : bypass * EACCES : discard packet. * ENOENT : ipsec_acquire() in progress, maybe. * others : error occured. */ #ifndef IP_FORWARDING -#define IP_FORWARDING 1 +#define IP_FORWARDING 1 #endif struct secpolicy * ipsec6_getpolicybyaddr(m, dir, flag, error) struct mbuf *m; u_int dir; int flag; int *error; { struct secpolicy *sp = NULL; /* sanity check */ if (m == NULL || error == NULL) panic("ipsec6_getpolicybyaddr: NULL pointer was passed.\n"); { struct secpolicyindex spidx; bzero(&spidx, sizeof(spidx)); /* make a index to look for a policy */ - if ((flag & IP_FORWARDING) == IP_FORWARDING) { - /* Case: IP forwarding */ - *error = ipsec_setspidx_mbuf(&spidx, dir, AF_INET6, m); - } else { - /* Case: ICMP echo reply */ - *error = ipsec_setspidx_mbuf(&spidx, dir, AF_INET6, m); - } + *error = ipsec_setspidx_mbuf(&spidx, dir, AF_INET6, m); if (*error != 0) return NULL; sp = key_allocsp(&spidx, dir); } /* SP found */ if (sp != NULL) { KEYDEBUG(KEYDEBUG_IPSEC_STAMP, printf("DP ipsec6_getpolicybyaddr called " "to allocate SP:%p\n", sp)); *error = 0; return sp; } /* no SP found */ if (ip6_def_policy.policy != IPSEC_POLICY_DISCARD && ip6_def_policy.policy != IPSEC_POLICY_NONE) { - printf("fixed system default policy:%d->%d\n", - ip6_def_policy.policy, - IPSEC_POLICY_NONE); + ipseclog((LOG_INFO, "fixed system default policy: %d->%d\n", + ip6_def_policy.policy, IPSEC_POLICY_NONE)); ip6_def_policy.policy = IPSEC_POLICY_NONE; } ip6_def_policy.refcnt++; *error = 0; return &ip6_def_policy; } #endif /* INET6 */ /* * set IP address into spidx from mbuf. * When Forwarding packet and ICMP echo reply, this function is used. * * IN: get the followings from mbuf. * protocol family, src, dst, next protocol * OUT: * 0: success. * other: failure, and set errno. */ int ipsec_setspidx_mbuf(spidx, dir, family, m) struct secpolicyindex *spidx; u_int dir, family; struct mbuf *m; { + /* sanity check */ if (spidx == NULL || m == NULL) panic("ipsec_setspidx_mbuf: NULL pointer was passed.\n"); KEYDEBUG(KEYDEBUG_IPSEC_DUMP, printf("ipsec_setspidx_mbuf: begin\n"); kdebug_mbuf(m)); /* initialize */ bzero(spidx, sizeof(*spidx)); spidx->dir = dir; - spidx->src.ss_len = spidx->dst.ss_len = _SALENBYAF(family); - spidx->src.ss_family = spidx->dst.ss_family = family; - spidx->prefs = spidx->prefd = _INALENBYAF(family) << 3; { /* sanity check for packet length. */ struct mbuf *n; int tlen; tlen = 0; for (n = m; n; n = n->m_next) tlen += n->m_len; if (m->m_pkthdr.len != tlen) { KEYDEBUG(KEYDEBUG_IPSEC_DUMP, printf("ipsec_setspidx_mbuf: " "total of m_len(%d) != pkthdr.len(%d), " "ignored.\n", tlen, m->m_pkthdr.len)); goto bad; } } switch (family) { case AF_INET: { struct ip *ip; struct ip ipbuf; + struct sockaddr_in *sin; /* sanity check 1 for minimum ip header length */ if (m->m_pkthdr.len < sizeof(struct ip)) { KEYDEBUG(KEYDEBUG_IPSEC_DUMP, printf("ipsec_setspidx_mbuf: " "pkthdr.len(%d) < sizeof(struct ip), " "ignored.\n", m->m_pkthdr.len)); goto bad; } /* * get IPv4 header packet. usually the mbuf is contiguous * and we need no copies. */ if (m->m_len >= sizeof(*ip)) ip = mtod(m, struct ip *); else { m_copydata(m, 0, sizeof(ipbuf), (caddr_t)&ipbuf); ip = &ipbuf; } - /* some more checks on IPv4 header. */ - bcopy(&ip->ip_src, _INADDRBYSA(&spidx->src), - sizeof(ip->ip_src)); - bcopy(&ip->ip_dst, _INADDRBYSA(&spidx->dst), - sizeof(ip->ip_dst)); + /* XXX some more checks on IPv4 header. */ + sin = (struct sockaddr_in *)&spidx->src; + sin->sin_family = AF_INET; + sin->sin_len = sizeof(*sin); + bcopy(&ip->ip_src, &sin->sin_addr, sizeof(sin->sin_addr)); + sin->sin_port = IPSEC_PORT_ANY; + + sin = (struct sockaddr_in *)&spidx->dst; + sin->sin_family = AF_INET; + sin->sin_len = sizeof(*sin); + bcopy(&ip->ip_dst, &sin->sin_addr, sizeof(sin->sin_addr)); + sin->sin_port = IPSEC_PORT_ANY; + + spidx->prefs = spidx->prefd = sizeof(struct in_addr) << 3; + spidx->ul_proto = ip->ip_p; - _INPORTBYSA(&spidx->src) = IPSEC_PORT_ANY; - _INPORTBYSA(&spidx->dst) = IPSEC_PORT_ANY; break; } #ifdef INET6 case AF_INET6: { - struct ip6_hdr *ip6_hdr; + struct ip6_hdr *ip6; struct ip6_hdr ip6buf; + struct sockaddr_in6 *sin6; /* sanity check 1 for minimum ip header length */ if (m->m_pkthdr.len < sizeof(struct ip6_hdr)) { KEYDEBUG(KEYDEBUG_IPSEC_DUMP, printf("ipsec_setspidx_mbuf: " "pkthdr.len(%d) < sizeof(struct ip6_hdr), " "ignored.\n", m->m_pkthdr.len)); goto bad; } /* * get IPv6 header packet. usually the mbuf is contiguous * and we need no copies. */ - if (m->m_len >= sizeof(*ip6_hdr)) - ip6_hdr = mtod(m, struct ip6_hdr *); + if (m->m_len >= sizeof(*ip6)) + ip6 = mtod(m, struct ip6_hdr *); else { m_copydata(m, 0, sizeof(ip6buf), (caddr_t)&ip6buf); - ip6_hdr = &ip6buf; + ip6 = &ip6buf; } /* some more checks on IPv4 header. */ - if ((ip6_hdr->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) { + if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) { KEYDEBUG(KEYDEBUG_IPSEC_DUMP, printf("ipsec_setspidx_mbuf: " "wrong ip version on packet " "(expected IPv6), ignored.\n")); goto bad; } - bcopy(&ip6_hdr->ip6_src, _INADDRBYSA(&spidx->src), - sizeof(ip6_hdr->ip6_src)); - bcopy(&ip6_hdr->ip6_dst, _INADDRBYSA(&spidx->dst), - sizeof(ip6_hdr->ip6_dst)); + sin6 = (struct sockaddr_in6 *)&spidx->src; + sin6->sin6_family = AF_INET6; + sin6->sin6_len = sizeof(*sin6); + bcopy(&ip6->ip6_src, &sin6->sin6_addr, sizeof(sin6->sin6_addr)); + sin6->sin6_port = IPSEC_PORT_ANY; + if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) { + /* fix scope id for comparing SPD */ + sin6->sin6_addr.s6_addr16[1] = 0; + sin6->sin6_scope_id = ntohs(ip6->ip6_src.s6_addr16[1]); + } - spidx->ul_proto = ipsec6_get_ulp(m); - _INPORTBYSA(&spidx->src) = IPSEC_PORT_ANY; - _INPORTBYSA(&spidx->dst) = IPSEC_PORT_ANY; + sin6 = (struct sockaddr_in6 *)&spidx->dst; + sin6->sin6_family = AF_INET6; + sin6->sin6_len = sizeof(*sin6); + bcopy(&ip6->ip6_dst, &sin6->sin6_addr, sizeof(sin6->sin6_addr)); + sin6->sin6_port = IPSEC_PORT_ANY; + if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) { + /* fix scope id for comparing SPD */ + sin6->sin6_addr.s6_addr16[1] = 0; + sin6->sin6_scope_id = ntohs(ip6->ip6_dst.s6_addr16[1]); + } + + spidx->prefs = spidx->prefd = sizeof(struct in6_addr) << 3; + + ipsec6_get_ulp(m, spidx); break; } #endif /* INET6 */ default: panic("ipsec_secsecidx: no supported family passed.\n"); } KEYDEBUG(KEYDEBUG_IPSEC_DUMP, printf("ipsec_setspidx_mbuf: end\n"); kdebug_secpolicyindex(spidx)); return 0; bad: /* XXX initialize */ bzero(spidx, sizeof(*spidx)); return EINVAL; } #ifdef INET6 /* - * Get the number of upper layer protocol. + * Get upper layer protocol number and port number if there. * Assumed all extension headers are in single mbuf. */ -static u_int16_t -ipsec6_get_ulp(m) +#include +#include +static void +ipsec6_get_ulp(m, spidx) struct mbuf *m; + struct secpolicyindex *spidx; { - struct ip6_hdr *ip6; - struct ip6_ext *ip6e; int off, nxt; - int len; /* sanity check */ if (m == NULL) panic("ipsec6_get_ulp: NULL pointer was passed.\n"); KEYDEBUG(KEYDEBUG_IPSEC_DUMP, printf("ipsec6_get_ulp:\n"); kdebug_mbuf(m)); - ip6 = mtod(m, struct ip6_hdr *); - nxt = ip6->ip6_nxt; - off = sizeof(struct ip6_hdr); - len = m->m_len; + /* set default */ + spidx->ul_proto = IPSEC_ULPROTO_ANY; + ((struct sockaddr_in6 *)&spidx->src)->sin6_port = IPSEC_PORT_ANY; + ((struct sockaddr_in6 *)&spidx->dst)->sin6_port = IPSEC_PORT_ANY; - while (off < len) { - ip6e = (struct ip6_ext *)((caddr_t) ip6 + off); - if (m->m_len < off + sizeof(*ip6e)) { - printf("ipsec6_get_ulp: all exthdr are not " - "in single mbuf.\n"); - return IPSEC_PORT_ANY; - } + nxt = -1; + off = ip6_lasthdr(m, 0, IPPROTO_IPV6, &nxt); + if (off < 0 || m->m_pkthdr.len < off) + return; - switch(nxt) { - case IPPROTO_TCP: - case IPPROTO_UDP: - case IPPROTO_ICMPV6: - return nxt; - case IPPROTO_FRAGMENT: - off += sizeof(struct ip6_frag); - break; - case IPPROTO_AH: - off += (ip6e->ip6e_len + 2) << 2; - break; - default: - switch (nxt) { - case IPPROTO_HOPOPTS: - case IPPROTO_ROUTING: - case IPPROTO_ESP: - case IPPROTO_NONE: - case IPPROTO_DSTOPTS: - break; - default: - return nxt; /* XXX */ - } - off += (ip6e->ip6e_len + 1) << 3; - break; + switch (nxt) { + case IPPROTO_TCP: + spidx->ul_proto = nxt; + if (off + sizeof(struct tcphdr) <= m->m_pkthdr.len) { + struct tcphdr th; + m_copydata(m, off, sizeof(th), (caddr_t)&th); + ((struct sockaddr_in6 *)&spidx->src)->sin6_port = + th.th_sport; + ((struct sockaddr_in6 *)&spidx->dst)->sin6_port = + th.th_dport; } - nxt = ip6e->ip6e_nxt; + break; + case IPPROTO_UDP: + spidx->ul_proto = nxt; + if (off + sizeof(struct udphdr) <= m->m_pkthdr.len) { + struct udphdr uh; + m_copydata(m, off, sizeof(uh), (caddr_t)&uh); + ((struct sockaddr_in6 *)&spidx->src)->sin6_port = + uh.uh_sport; + ((struct sockaddr_in6 *)&spidx->dst)->sin6_port = + uh.uh_dport; + } + break; + case IPPROTO_ICMPV6: + spidx->ul_proto = nxt; + break; + default: + break; } - - return IPSEC_PORT_ANY; } #endif static void ipsec4_setspidx_inpcb(m, pcb) struct mbuf *m; struct inpcb *pcb; { struct secpolicyindex *spidx; + struct sockaddr_in *sin1, *sin2; /* sanity check */ if (pcb == NULL) panic("ipsec4_setspidx_inpcb: no PCB found.\n"); if (pcb->inp_sp == NULL) panic("ipsec4_setspidx_inpcb: no inp_sp found.\n"); if (pcb->inp_sp->sp_out ==NULL || pcb->inp_sp->sp_in == NULL) panic("ipsec4_setspidx_inpcb: no sp_in/out found.\n"); bzero(&pcb->inp_sp->sp_in->spidx, sizeof(*spidx)); bzero(&pcb->inp_sp->sp_out->spidx, sizeof(*spidx)); spidx = &pcb->inp_sp->sp_in->spidx; spidx->dir = IPSEC_DIR_INBOUND; - spidx->src.ss_len = spidx->dst.ss_len = _SALENBYAF(AF_INET); - spidx->src.ss_family = spidx->dst.ss_family = AF_INET; - spidx->prefs = _INALENBYAF(AF_INET) << 3; - spidx->prefd = _INALENBYAF(AF_INET) << 3; + sin1 = (struct sockaddr_in *)&spidx->src; + sin2 = (struct sockaddr_in *)&spidx->dst; + sin1->sin_len = sin2->sin_len = sizeof(struct sockaddr_in); + sin1->sin_family = sin2->sin_family = AF_INET; + spidx->prefs = sizeof(struct in_addr) << 3; + spidx->prefd = sizeof(struct in_addr) << 3; spidx->ul_proto = pcb->inp_socket->so_proto->pr_protocol; - _INPORTBYSA(&spidx->src) = pcb->inp_fport; - _INPORTBYSA(&spidx->dst) = pcb->inp_lport; + sin1->sin_port = pcb->inp_fport; + sin2->sin_port = pcb->inp_lport; ipsec4_setspidx_ipaddr(m, spidx); spidx = &pcb->inp_sp->sp_out->spidx; spidx->dir = IPSEC_DIR_OUTBOUND; - spidx->src.ss_len = spidx->dst.ss_len = _SALENBYAF(AF_INET); - spidx->src.ss_family = spidx->dst.ss_family = AF_INET; - spidx->prefs = _INALENBYAF(AF_INET) << 3; - spidx->prefd = _INALENBYAF(AF_INET) << 3; + sin1 = (struct sockaddr_in *)&spidx->src; + sin2 = (struct sockaddr_in *)&spidx->dst; + sin1->sin_len = sin2->sin_len = sizeof(struct sockaddr_in); + sin1->sin_family = sin2->sin_family = AF_INET; + spidx->prefs = sizeof(struct in_addr) << 3; + spidx->prefd = sizeof(struct in_addr) << 3; spidx->ul_proto = pcb->inp_socket->so_proto->pr_protocol; - _INPORTBYSA(&spidx->src) = pcb->inp_lport; - _INPORTBYSA(&spidx->dst) = pcb->inp_fport; + sin1->sin_port = pcb->inp_lport; + sin2->sin_port = pcb->inp_fport; ipsec4_setspidx_ipaddr(m, spidx); return; } static void ipsec4_setspidx_ipaddr(m, spidx) struct mbuf *m; struct secpolicyindex *spidx; { struct ip *ip = NULL; + struct ip ipbuf; /* sanity check 1 for minimum ip header length */ if (m == NULL) - panic("ipsec4_setspidx_in6pcb: m == 0 passed.\n"); + panic("ipsec4_setspidx_ipaddr: m == 0 passed.\n"); if (m->m_pkthdr.len < sizeof(struct ip)) { KEYDEBUG(KEYDEBUG_IPSEC_DUMP, printf("ipsec4_setspidx_ipaddr: " "pkthdr.len(%d) < sizeof(struct ip), " "ignored.\n", m->m_pkthdr.len)); return; } if (m->m_len >= sizeof(*ip)) ip = mtod(m, struct ip *); else { - struct ip ipbuf; - m_copydata(m, 0, sizeof(ipbuf), (caddr_t)&ipbuf); ip = &ipbuf; } - bcopy(&ip->ip_src, _INADDRBYSA(&spidx->src), sizeof(ip->ip_src)); - bcopy(&ip->ip_dst, _INADDRBYSA(&spidx->dst), sizeof(ip->ip_dst)); + bcopy(&ip->ip_src, &((struct sockaddr_in *)&spidx->src)->sin_addr, + sizeof(ip->ip_src)); + bcopy(&ip->ip_dst, &((struct sockaddr_in *)&spidx->dst)->sin_addr, + sizeof(ip->ip_dst)); return; } #ifdef INET6 static void ipsec6_setspidx_in6pcb(m, pcb) struct mbuf *m; struct in6pcb *pcb; { struct secpolicyindex *spidx; + struct sockaddr_in6 *sin1, *sin2; /* sanity check */ if (pcb == NULL) panic("ipsec6_setspidx_in6pcb: no PCB found.\n"); if (pcb->in6p_sp == NULL) panic("ipsec6_setspidx_in6pcb: no in6p_sp found.\n"); if (pcb->in6p_sp->sp_out ==NULL || pcb->in6p_sp->sp_in == NULL) panic("ipsec6_setspidx_in6pcb: no sp_in/out found.\n"); bzero(&pcb->in6p_sp->sp_in->spidx, sizeof(*spidx)); bzero(&pcb->in6p_sp->sp_out->spidx, sizeof(*spidx)); spidx = &pcb->in6p_sp->sp_in->spidx; spidx->dir = IPSEC_DIR_INBOUND; - spidx->src.ss_len = spidx->dst.ss_len = _SALENBYAF(AF_INET6); - spidx->src.ss_family = spidx->dst.ss_family = AF_INET6; - spidx->prefs = _INALENBYAF(AF_INET6) << 3; - spidx->prefd = _INALENBYAF(AF_INET6) << 3; + sin1 = (struct sockaddr_in6 *)&spidx->src; + sin2 = (struct sockaddr_in6 *)&spidx->dst; + sin1->sin6_len = sin2->sin6_len = sizeof(struct sockaddr_in6); + sin1->sin6_family = sin2->sin6_family = AF_INET6; + spidx->prefs = sizeof(struct in6_addr) << 3; + spidx->prefd = sizeof(struct in6_addr) << 3; spidx->ul_proto = pcb->in6p_socket->so_proto->pr_protocol; - _INPORTBYSA(&spidx->src) = pcb->in6p_fport; - _INPORTBYSA(&spidx->dst) = pcb->in6p_lport; + sin1->sin6_port = pcb->in6p_fport; + sin2->sin6_port = pcb->in6p_lport; ipsec6_setspidx_ipaddr(m, spidx); spidx = &pcb->in6p_sp->sp_out->spidx; spidx->dir = IPSEC_DIR_OUTBOUND; - spidx->src.ss_len = spidx->dst.ss_len = _SALENBYAF(AF_INET6); - spidx->src.ss_family = spidx->dst.ss_family = AF_INET6; - spidx->prefs = _INALENBYAF(AF_INET6) << 3; - spidx->prefd = _INALENBYAF(AF_INET6) << 3; + sin1 = (struct sockaddr_in6 *)&spidx->src; + sin2 = (struct sockaddr_in6 *)&spidx->dst; + sin1->sin6_len = sin2->sin6_len = sizeof(struct sockaddr_in6); + sin1->sin6_family = sin2->sin6_family = AF_INET6; + spidx->prefs = sizeof(struct in6_addr) << 3; + spidx->prefd = sizeof(struct in6_addr) << 3; spidx->ul_proto = pcb->in6p_socket->so_proto->pr_protocol; - _INPORTBYSA(&spidx->src) = pcb->in6p_lport; - _INPORTBYSA(&spidx->dst) = pcb->in6p_fport; + sin1->sin6_port = pcb->in6p_lport; + sin2->sin6_port = pcb->in6p_fport; ipsec6_setspidx_ipaddr(m, spidx); return; } static void ipsec6_setspidx_ipaddr(m, spidx) struct mbuf *m; struct secpolicyindex *spidx; { - struct ip6_hdr *ip6_hdr = NULL; + struct ip6_hdr *ip6 = NULL; + struct ip6_hdr ip6buf; + struct sockaddr_in6 *sin6; /* sanity check 1 for minimum ip header length */ if (m == NULL) panic("ipsec6_setspidx_in6pcb: m == 0 passed.\n"); if (m->m_pkthdr.len < sizeof(struct ip6_hdr)) { KEYDEBUG(KEYDEBUG_IPSEC_DUMP, printf("ipsec6_setspidx_ipaddr: " "pkthdr.len(%d) < sizeof(struct ip6_hdr), " "ignored.\n", m->m_pkthdr.len)); return; } - if (m->m_len >= sizeof(*ip6_hdr)) - ip6_hdr = mtod(m, struct ip6_hdr *); + if (m->m_len >= sizeof(*ip6)) + ip6 = mtod(m, struct ip6_hdr *); else { - struct ip6_hdr ip6buf; - m_copydata(m, 0, sizeof(ip6buf), (caddr_t)&ip6buf); - ip6_hdr = &ip6buf; + ip6 = &ip6buf; } - if ((ip6_hdr->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) { + if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) { KEYDEBUG(KEYDEBUG_IPSEC_DUMP, printf("ipsec_setspidx_mbuf: " "wrong ip version on packet " "(expected IPv6), ignored.\n")); return; } - bcopy(&ip6_hdr->ip6_src, _INADDRBYSA(&spidx->src), - sizeof(ip6_hdr->ip6_src)); - bcopy(&ip6_hdr->ip6_dst, _INADDRBYSA(&spidx->dst), - sizeof(ip6_hdr->ip6_dst)); + sin6 = (struct sockaddr_in6 *)&spidx->src; + bcopy(&ip6->ip6_src, &sin6->sin6_addr, sizeof(ip6->ip6_src)); + if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) { + sin6->sin6_addr.s6_addr16[1] = 0; + sin6->sin6_scope_id = ntohs(ip6->ip6_src.s6_addr16[1]); + } + sin6 = (struct sockaddr_in6 *)&spidx->dst; + bcopy(&ip6->ip6_dst, &sin6->sin6_addr, sizeof(ip6->ip6_dst)); + if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) { + sin6->sin6_addr.s6_addr16[1] = 0; + sin6->sin6_scope_id = ntohs(ip6->ip6_dst.s6_addr16[1]); + } + return; } #endif +static struct inpcbpolicy * +ipsec_newpcbpolicy() +{ + struct inpcbpolicy *p; + + p = (struct inpcbpolicy *)malloc(sizeof(*p), M_SECA, M_NOWAIT); + return p; +} + +static void +ipsec_delpcbpolicy(p) + struct inpcbpolicy *p; +{ + free(p, M_SECA); +} + /* initialize policy in PCB */ int ipsec_init_policy(so, pcb_sp) struct socket *so; struct inpcbpolicy **pcb_sp; { struct inpcbpolicy *new; /* sanity check. */ if (so == NULL || pcb_sp == NULL) panic("ipsec_init_policy: NULL pointer was passed.\n"); - KMALLOC(new, struct inpcbpolicy *, sizeof(*new)); + new = ipsec_newpcbpolicy(); if (new == NULL) { - printf("ipsec_init_policy: No more memory.\n"); + ipseclog((LOG_DEBUG, "ipsec_init_policy: No more memory.\n")); return ENOBUFS; } bzero(new, sizeof(*new)); if (so->so_cred != 0 && so->so_cred->cr_uid == 0) new->priv = 1; else new->priv = 0; - KEYDEBUG(KEYDEBUG_IPSEC_DATA, - printf("init: priv=%d ", new->priv); - if (so->so_cred) - printf("cr_uid=%d\n", so->so_cred->cr_uid); - else - printf("so_cred is NULL\n"); - ); - if ((new->sp_in = key_newsp()) == NULL) { - KFREE(new); + ipsec_delpcbpolicy(new); return ENOBUFS; } new->sp_in->state = IPSEC_SPSTATE_ALIVE; new->sp_in->policy = IPSEC_POLICY_ENTRUST; if ((new->sp_out = key_newsp()) == NULL) { key_freesp(new->sp_in); - KFREE(new); + ipsec_delpcbpolicy(new); return ENOBUFS; } new->sp_out->state = IPSEC_SPSTATE_ALIVE; new->sp_out->policy = IPSEC_POLICY_ENTRUST; *pcb_sp = new; return 0; } /* copy old ipsec policy into new */ int ipsec_copy_policy(old, new) struct inpcbpolicy *old, *new; { struct secpolicy *sp; sp = ipsec_deepcopy_policy(old->sp_in); if (sp) { key_freesp(new->sp_in); new->sp_in = sp; } else return ENOBUFS; sp = ipsec_deepcopy_policy(old->sp_out); if (sp) { key_freesp(new->sp_out); new->sp_out = sp; } else return ENOBUFS; new->priv = old->priv; return 0; } /* deep-copy a policy in PCB */ static struct secpolicy * ipsec_deepcopy_policy(src) struct secpolicy *src; { struct ipsecrequest *newchain = NULL; struct ipsecrequest *p; struct ipsecrequest **q; struct ipsecrequest *r; struct secpolicy *dst; dst = key_newsp(); if (src == NULL || dst == NULL) return NULL; /* * deep-copy IPsec request chain. This is required since struct * ipsecrequest is not reference counted. */ q = &newchain; for (p = src->req; p; p = p->next) { - KMALLOC(*q, struct ipsecrequest *, sizeof(struct ipsecrequest)); + *q = (struct ipsecrequest *)malloc(sizeof(struct ipsecrequest), + M_SECA, M_NOWAIT); if (*q == NULL) goto fail; bzero(*q, sizeof(**q)); (*q)->next = NULL; (*q)->saidx.proto = p->saidx.proto; (*q)->saidx.mode = p->saidx.mode; (*q)->level = p->level; + (*q)->saidx.reqid = p->saidx.reqid; bcopy(&p->saidx.src, &(*q)->saidx.src, sizeof((*q)->saidx.src)); bcopy(&p->saidx.dst, &(*q)->saidx.dst, sizeof((*q)->saidx.dst)); (*q)->sav = NULL; (*q)->sp = dst; q = &((*q)->next); } dst->req = newchain; dst->state = src->state; dst->policy = src->policy; /* do not touch the refcnt fields */ return dst; fail: for (p = newchain; p; p = r) { r = p->next; - KFREE(p); + free(p, M_SECA); p = NULL; } return NULL; } /* set policy and ipsec request if present. */ static int -ipsec_set_policy(pcb_sp, optname, request, priv) +ipsec_set_policy(pcb_sp, optname, request, len, priv) struct secpolicy **pcb_sp; int optname; caddr_t request; + size_t len; int priv; { - struct sadb_x_policy *xpl = (struct sadb_x_policy *)request; + struct sadb_x_policy *xpl; struct secpolicy *newsp = NULL; + int error; /* sanity check. */ - if (pcb_sp == NULL || *pcb_sp == NULL || xpl == NULL) + if (pcb_sp == NULL || *pcb_sp == NULL || request == NULL) return EINVAL; + if (len < sizeof(*xpl)) + return EINVAL; + xpl = (struct sadb_x_policy *)request; KEYDEBUG(KEYDEBUG_IPSEC_DUMP, printf("ipsec_set_policy: passed policy\n"); kdebug_sadb_x_policy((struct sadb_ext *)xpl)); /* check policy type */ /* ipsec_set_policy() accepts IPSEC, ENTRUST and BYPASS. */ if (xpl->sadb_x_policy_type == IPSEC_POLICY_DISCARD || xpl->sadb_x_policy_type == IPSEC_POLICY_NONE) return EINVAL; /* check privileged socket */ if (priv == 0 && xpl->sadb_x_policy_type == IPSEC_POLICY_BYPASS) return EACCES; /* allocation new SP entry */ - if ((newsp = key_msg2sp(xpl)) == NULL) - return EINVAL; /* maybe ENOBUFS */ + if ((newsp = key_msg2sp(xpl, len, &error)) == NULL) + return error; newsp->state = IPSEC_SPSTATE_ALIVE; /* clear old SP and set new SP */ key_freesp(*pcb_sp); *pcb_sp = newsp; KEYDEBUG(KEYDEBUG_IPSEC_DUMP, printf("ipsec_set_policy: new policy\n"); kdebug_secpolicy(newsp)); return 0; } static int ipsec_get_policy(pcb_sp, mp) struct secpolicy *pcb_sp; struct mbuf **mp; { - struct sadb_x_policy *xpl; /* sanity check. */ if (pcb_sp == NULL || mp == NULL) return EINVAL; - if ((xpl = key_sp2msg(pcb_sp)) == NULL) { - printf("ipsec_get_policy: No more memory.\n"); + *mp = key_sp2msg(pcb_sp); + if (!*mp) { + ipseclog((LOG_DEBUG, "ipsec_get_policy: No more memory.\n")); return ENOBUFS; } - *mp = m_get(M_WAIT, MT_DATA); - (*mp)->m_len = PFKEY_EXTLEN(xpl); - m_copyback((*mp), 0, PFKEY_EXTLEN(xpl), (caddr_t)xpl); + (*mp)->m_type = MT_DATA; KEYDEBUG(KEYDEBUG_IPSEC_DUMP, printf("ipsec_get_policy:\n"); kdebug_mbuf(*mp)); - KFREE(xpl); - return 0; } int -ipsec4_set_policy(inp, optname, request, priv) +ipsec4_set_policy(inp, optname, request, len, priv) struct inpcb *inp; int optname; caddr_t request; + size_t len; int priv; { - struct sadb_x_policy *xpl = (struct sadb_x_policy *)request; + struct sadb_x_policy *xpl; struct secpolicy **pcb_sp; /* sanity check. */ if (inp == NULL || request == NULL) return EINVAL; + if (len < sizeof(*xpl)) + return EINVAL; + xpl = (struct sadb_x_policy *)request; /* select direction */ switch (xpl->sadb_x_policy_dir) { case IPSEC_DIR_INBOUND: pcb_sp = &inp->inp_sp->sp_in; break; case IPSEC_DIR_OUTBOUND: pcb_sp = &inp->inp_sp->sp_out; break; default: - printf("ipsec4_set_policy: invalid direction=%u\n", - xpl->sadb_x_policy_dir); + ipseclog((LOG_ERR, "ipsec4_set_policy: invalid direction=%u\n", + xpl->sadb_x_policy_dir)); return EINVAL; } - return ipsec_set_policy(pcb_sp, optname, request, priv); + return ipsec_set_policy(pcb_sp, optname, request, len, priv); } int -ipsec4_get_policy(inp, request, mp) +ipsec4_get_policy(inp, request, len, mp) struct inpcb *inp; caddr_t request; + size_t len; struct mbuf **mp; { - struct sadb_x_policy *xpl = (struct sadb_x_policy *)request; + struct sadb_x_policy *xpl; struct secpolicy *pcb_sp; /* sanity check. */ if (inp == NULL || request == NULL || mp == NULL) return EINVAL; + if (inp->inp_sp == NULL) + panic("policy in PCB is NULL\n"); + if (len < sizeof(*xpl)) + return EINVAL; + xpl = (struct sadb_x_policy *)request; /* select direction */ switch (xpl->sadb_x_policy_dir) { case IPSEC_DIR_INBOUND: pcb_sp = inp->inp_sp->sp_in; break; case IPSEC_DIR_OUTBOUND: pcb_sp = inp->inp_sp->sp_out; break; default: - printf("ipsec6_set_policy: invalid direction=%u\n", - xpl->sadb_x_policy_dir); + ipseclog((LOG_ERR, "ipsec4_set_policy: invalid direction=%u\n", + xpl->sadb_x_policy_dir)); return EINVAL; } return ipsec_get_policy(pcb_sp, mp); } /* delete policy in PCB */ int ipsec4_delete_pcbpolicy(inp) struct inpcb *inp; { /* sanity check. */ if (inp == NULL) panic("ipsec4_delete_pcbpolicy: NULL pointer was passed.\n"); + if (inp->inp_sp == NULL) + return 0; + if (inp->inp_sp->sp_in != NULL) { key_freesp(inp->inp_sp->sp_in); inp->inp_sp->sp_in = NULL; } if (inp->inp_sp->sp_out != NULL) { key_freesp(inp->inp_sp->sp_out); inp->inp_sp->sp_out = NULL; } - KFREE(inp->inp_sp); + ipsec_delpcbpolicy(inp->inp_sp); inp->inp_sp = NULL; return 0; } #ifdef INET6 int -ipsec6_set_policy(in6p, optname, request, priv) +ipsec6_set_policy(in6p, optname, request, len, priv) struct in6pcb *in6p; int optname; caddr_t request; + size_t len; int priv; { - struct sadb_x_policy *xpl = (struct sadb_x_policy *)request; + struct sadb_x_policy *xpl; struct secpolicy **pcb_sp; /* sanity check. */ if (in6p == NULL || request == NULL) return EINVAL; + if (len < sizeof(*xpl)) + return EINVAL; + xpl = (struct sadb_x_policy *)request; /* select direction */ switch (xpl->sadb_x_policy_dir) { case IPSEC_DIR_INBOUND: pcb_sp = &in6p->in6p_sp->sp_in; break; case IPSEC_DIR_OUTBOUND: pcb_sp = &in6p->in6p_sp->sp_out; break; default: - printf("ipsec6_set_policy: invalid direction=%u\n", - xpl->sadb_x_policy_dir); + ipseclog((LOG_ERR, "ipsec6_set_policy: invalid direction=%u\n", + xpl->sadb_x_policy_dir)); return EINVAL; } - return ipsec_set_policy(pcb_sp, optname, request, priv); + return ipsec_set_policy(pcb_sp, optname, request, len, priv); } int -ipsec6_get_policy(in6p, request, mp) +ipsec6_get_policy(in6p, request, len, mp) struct in6pcb *in6p; caddr_t request; + size_t len; struct mbuf **mp; { - struct sadb_x_policy *xpl = (struct sadb_x_policy *)request; + struct sadb_x_policy *xpl; struct secpolicy *pcb_sp; /* sanity check. */ if (in6p == NULL || request == NULL || mp == NULL) return EINVAL; + if (in6p->in6p_sp == NULL) + panic("policy in PCB is NULL\n"); + if (len < sizeof(*xpl)) + return EINVAL; + xpl = (struct sadb_x_policy *)request; /* select direction */ switch (xpl->sadb_x_policy_dir) { case IPSEC_DIR_INBOUND: pcb_sp = in6p->in6p_sp->sp_in; break; case IPSEC_DIR_OUTBOUND: pcb_sp = in6p->in6p_sp->sp_out; break; default: - printf("ipsec6_set_policy: invalid direction=%u\n", - xpl->sadb_x_policy_dir); + ipseclog((LOG_ERR, "ipsec6_set_policy: invalid direction=%u\n", + xpl->sadb_x_policy_dir)); return EINVAL; } return ipsec_get_policy(pcb_sp, mp); } int ipsec6_delete_pcbpolicy(in6p) struct in6pcb *in6p; { /* sanity check. */ if (in6p == NULL) panic("ipsec6_delete_pcbpolicy: NULL pointer was passed.\n"); + if (in6p->in6p_sp == NULL) + return 0; + if (in6p->in6p_sp->sp_in != NULL) { key_freesp(in6p->in6p_sp->sp_in); in6p->in6p_sp->sp_in = NULL; } if (in6p->in6p_sp->sp_out != NULL) { key_freesp(in6p->in6p_sp->sp_out); in6p->in6p_sp->sp_out = NULL; } - KFREE(in6p->in6p_sp); + ipsec_delpcbpolicy(in6p->in6p_sp); in6p->in6p_sp = NULL; return 0; } #endif /* * return current level. - * IPSEC_LEVEL_USE or IPSEC_LEVEL_REQUIRE are always returned. + * Either IPSEC_LEVEL_USE or IPSEC_LEVEL_REQUIRE are always returned. */ u_int ipsec_get_reqlevel(isr) struct ipsecrequest *isr; { u_int level = 0; u_int esp_trans_deflev, esp_net_deflev, ah_trans_deflev, ah_net_deflev; /* sanity check */ if (isr == NULL || isr->sp == NULL) panic("ipsec_get_reqlevel: NULL pointer is passed.\n"); - if (isr->sp->spidx.src.ss_family != isr->sp->spidx.dst.ss_family) + if (((struct sockaddr *)&isr->sp->spidx.src)->sa_family + != ((struct sockaddr *)&isr->sp->spidx.dst)->sa_family) panic("ipsec_get_reqlevel: family mismatched.\n"); -#define IPSEC_CHECK_DEFAULT(lev) \ - (((lev) != IPSEC_LEVEL_USE && (lev) != IPSEC_LEVEL_REQUIRE) \ - ? (printf("fixed system default level " #lev ":%d->%d\n", \ - (lev), IPSEC_LEVEL_USE), \ - (lev) = IPSEC_LEVEL_USE) : (lev)) +/* XXX note that we have ipseclog() expanded here - code sync issue */ +#define IPSEC_CHECK_DEFAULT(lev) \ + (((lev) != IPSEC_LEVEL_USE && (lev) != IPSEC_LEVEL_REQUIRE \ + && (lev) != IPSEC_LEVEL_UNIQUE) \ + ? (ipsec_debug \ + ? log(LOG_INFO, "fixed system default level " #lev ":%d->%d\n",\ + (lev), IPSEC_LEVEL_REQUIRE) \ + : 0), \ + (lev) = IPSEC_LEVEL_REQUIRE, \ + (lev) \ + : (lev)) /* set default level */ - switch (isr->sp->spidx.src.ss_family) { + switch (((struct sockaddr *)&isr->sp->spidx.src)->sa_family) { #ifdef INET case AF_INET: esp_trans_deflev = IPSEC_CHECK_DEFAULT(ip4_esp_trans_deflev); esp_net_deflev = IPSEC_CHECK_DEFAULT(ip4_esp_net_deflev); ah_trans_deflev = IPSEC_CHECK_DEFAULT(ip4_ah_trans_deflev); ah_net_deflev = IPSEC_CHECK_DEFAULT(ip4_ah_net_deflev); break; #endif #ifdef INET6 case AF_INET6: esp_trans_deflev = IPSEC_CHECK_DEFAULT(ip6_esp_trans_deflev); esp_net_deflev = IPSEC_CHECK_DEFAULT(ip6_esp_net_deflev); ah_trans_deflev = IPSEC_CHECK_DEFAULT(ip6_ah_trans_deflev); ah_net_deflev = IPSEC_CHECK_DEFAULT(ip6_ah_net_deflev); break; #endif /* INET6 */ default: panic("key_get_reqlevel: Unknown family. %d\n", - isr->sp->spidx.src.ss_family); + ((struct sockaddr *)&isr->sp->spidx.src)->sa_family); } -#undef IPSEC_CHECK_DEFAULT(lev) +#undef IPSEC_CHECK_DEFAULT /* set level */ switch (isr->level) { case IPSEC_LEVEL_DEFAULT: switch (isr->saidx.proto) { case IPPROTO_ESP: if (isr->saidx.mode == IPSEC_MODE_TUNNEL) level = esp_net_deflev; else level = esp_trans_deflev; break; case IPPROTO_AH: if (isr->saidx.mode == IPSEC_MODE_TUNNEL) level = ah_net_deflev; else level = ah_trans_deflev; + case IPPROTO_IPCOMP: + /* + * we don't really care, as IPcomp document says that + * we shouldn't compress small packets + */ + level = IPSEC_LEVEL_USE; + break; default: panic("ipsec_get_reqlevel: " "Illegal protocol defined %u\n", isr->saidx.proto); } break; case IPSEC_LEVEL_USE: case IPSEC_LEVEL_REQUIRE: level = isr->level; break; + case IPSEC_LEVEL_UNIQUE: + level = IPSEC_LEVEL_REQUIRE; + break; default: panic("ipsec_get_reqlevel: Illegal IPsec level %u\n", isr->level); } return level; } /* * Check AH/ESP integrity. * OUT: * 0: valid * 1: invalid */ static int ipsec_in_reject(sp, m) struct secpolicy *sp; struct mbuf *m; { struct ipsecrequest *isr; u_int level; int need_auth, need_conf, need_icv; KEYDEBUG(KEYDEBUG_IPSEC_DATA, printf("ipsec_in_reject: using SP\n"); kdebug_secpolicy(sp)); /* check policy */ switch (sp->policy) { case IPSEC_POLICY_DISCARD: return 1; case IPSEC_POLICY_BYPASS: case IPSEC_POLICY_NONE: return 0; - + case IPSEC_POLICY_IPSEC: break; case IPSEC_POLICY_ENTRUST: default: panic("ipsec_hdrsiz: Invalid policy found. %d\n", sp->policy); } need_auth = 0; need_conf = 0; need_icv = 0; for (isr = sp->req; isr != NULL; isr = isr->next) { /* get current level */ level = ipsec_get_reqlevel(isr); switch (isr->saidx.proto) { case IPPROTO_ESP: if (level == IPSEC_LEVEL_REQUIRE) { need_conf++; if (isr->sav != NULL && isr->sav->flags == SADB_X_EXT_NONE && isr->sav->alg_auth != SADB_AALG_NONE) need_icv++; } break; case IPPROTO_AH: if (level == IPSEC_LEVEL_REQUIRE) { need_auth++; need_icv++; } break; + case IPPROTO_IPCOMP: + /* + * we don't really care, as IPcomp document says that + * we shouldn't compress small packets + */ + break; } } KEYDEBUG(KEYDEBUG_IPSEC_DUMP, printf("ipsec_in_reject: auth:%d conf:%d icv:%d m_flags:%x\n", need_auth, need_conf, need_icv, m->m_flags)); if ((need_conf && !(m->m_flags & M_DECRYPTED)) || (!need_auth && need_icv && !(m->m_flags & M_AUTHIPDGM)) || (need_auth && !(m->m_flags & M_AUTHIPHDR))) return 1; return 0; } /* * Check AH/ESP integrity. * This function is called from tcp_input(), udp_input(), * and {ah,esp}4_input for tunnel mode */ int ipsec4_in_reject_so(m, so) struct mbuf *m; struct socket *so; { struct secpolicy *sp = NULL; int error; int result; /* sanity check */ if (m == NULL) return 0; /* XXX should be panic ? */ /* get SP for this packet. * When we are called from ip_forward(), we call * ipsec4_getpolicybyaddr() with IP_FORWARDING flag. */ if (so == NULL) sp = ipsec4_getpolicybyaddr(m, IPSEC_DIR_INBOUND, IP_FORWARDING, &error); else sp = ipsec4_getpolicybysock(m, IPSEC_DIR_INBOUND, so, &error); if (sp == NULL) return 0; /* XXX should be panic ? * -> No, there may be error. */ result = ipsec_in_reject(sp, m); KEYDEBUG(KEYDEBUG_IPSEC_STAMP, printf("DP ipsec4_in_reject_so call free SP:%p\n", sp)); key_freesp(sp); return result; } int ipsec4_in_reject(m, inp) struct mbuf *m; struct inpcb *inp; { if (inp == NULL) return ipsec4_in_reject_so(m, NULL); else { if (inp->inp_socket) return ipsec4_in_reject_so(m, inp->inp_socket); else panic("ipsec4_in_reject: invalid inpcb/socket"); } } #ifdef INET6 /* * Check AH/ESP integrity. * This function is called from tcp6_input(), udp6_input(), * and {ah,esp}6_input for tunnel mode */ int ipsec6_in_reject_so(m, so) struct mbuf *m; struct socket *so; { struct secpolicy *sp = NULL; int error; int result; /* sanity check */ if (m == NULL) return 0; /* XXX should be panic ? */ /* get SP for this packet. * When we are called from ip_forward(), we call * ipsec6_getpolicybyaddr() with IP_FORWARDING flag. */ if (so == NULL) sp = ipsec6_getpolicybyaddr(m, IPSEC_DIR_INBOUND, IP_FORWARDING, &error); else sp = ipsec6_getpolicybysock(m, IPSEC_DIR_INBOUND, so, &error); if (sp == NULL) return 0; /* XXX should be panic ? */ result = ipsec_in_reject(sp, m); KEYDEBUG(KEYDEBUG_IPSEC_STAMP, printf("DP ipsec6_in_reject_so call free SP:%p\n", sp)); key_freesp(sp); return result; } int ipsec6_in_reject(m, in6p) struct mbuf *m; struct in6pcb *in6p; { if (in6p == NULL) return ipsec6_in_reject_so(m, NULL); else { if (in6p->in6p_socket) return ipsec6_in_reject_so(m, in6p->in6p_socket); else panic("ipsec6_in_reject: invalid in6p/socket"); } } #endif /* * compute the byte size to be occupied by IPsec header. * in case it is tunneled, it includes the size of outer IP header. * NOTE: SP passed is free in this function. */ static size_t ipsec_hdrsiz(sp) struct secpolicy *sp; { struct ipsecrequest *isr; size_t siz, clen; KEYDEBUG(KEYDEBUG_IPSEC_DATA, printf("ipsec_in_reject: using SP\n"); kdebug_secpolicy(sp)); /* check policy */ switch (sp->policy) { case IPSEC_POLICY_DISCARD: case IPSEC_POLICY_BYPASS: case IPSEC_POLICY_NONE: return 0; - + case IPSEC_POLICY_IPSEC: break; case IPSEC_POLICY_ENTRUST: default: panic("ipsec_hdrsiz: Invalid policy found. %d\n", sp->policy); } siz = 0; for (isr = sp->req; isr != NULL; isr = isr->next) { clen = 0; switch (isr->saidx.proto) { case IPPROTO_ESP: #ifdef IPSEC_ESP clen = esp_hdrsiz(isr); #else clen = 0; /*XXX*/ #endif break; case IPPROTO_AH: clen = ah_hdrsiz(isr); break; + case IPPROTO_IPCOMP: + clen = sizeof(struct ipcomp); + break; } if (isr->saidx.mode == IPSEC_MODE_TUNNEL) { - switch (isr->saidx.dst.ss_family) { + switch (((struct sockaddr *)&isr->saidx.dst)->sa_family) { case AF_INET: clen += sizeof(struct ip); break; #ifdef INET6 case AF_INET6: clen += sizeof(struct ip6_hdr); break; #endif default: - printf("ipsec_hdrsiz: unknown AF %d " - "in IPsec tunnel SA\n", - isr->saidx.dst.ss_family); + ipseclog((LOG_ERR, "ipsec_hdrsiz: " + "unknown AF %d in IPsec tunnel SA\n", + ((struct sockaddr *)&isr->saidx.dst)->sa_family)); break; } } siz += clen; } return siz; } /* This function is called from ip_forward() and ipsec4_hdrsize_tcp(). */ size_t ipsec4_hdrsiz(m, dir, inp) struct mbuf *m; u_int dir; struct inpcb *inp; { struct secpolicy *sp = NULL; int error; size_t size; /* sanity check */ if (m == NULL) return 0; /* XXX should be panic ? */ if (inp != NULL && inp->inp_socket == NULL) panic("ipsec4_hdrsize: why is socket NULL but there is PCB."); /* get SP for this packet. * When we are called from ip_forward(), we call * ipsec4_getpolicybyaddr() with IP_FORWARDING flag. */ if (inp == NULL) sp = ipsec4_getpolicybyaddr(m, dir, IP_FORWARDING, &error); else sp = ipsec4_getpolicybysock(m, dir, inp->inp_socket, &error); if (sp == NULL) return 0; /* XXX should be panic ? */ size = ipsec_hdrsiz(sp); KEYDEBUG(KEYDEBUG_IPSEC_STAMP, printf("DP ipsec4_hdrsiz call free SP:%p\n", sp)); KEYDEBUG(KEYDEBUG_IPSEC_DATA, printf("ipsec4_hdrsiz: size:%lu.\n", (unsigned long)size)); key_freesp(sp); return size; } #ifdef INET6 /* This function is called from ipsec6_hdrsize_tcp(), * and maybe from ip6_forward.() */ size_t ipsec6_hdrsiz(m, dir, in6p) struct mbuf *m; u_int dir; struct in6pcb *in6p; { struct secpolicy *sp = NULL; int error; size_t size; /* sanity check */ if (m == NULL) return 0; /* XXX shoud be panic ? */ if (in6p != NULL && in6p->in6p_socket == NULL) panic("ipsec6_hdrsize: why is socket NULL but there is PCB."); /* get SP for this packet */ /* XXX Is it right to call with IP_FORWARDING. */ if (in6p == NULL) sp = ipsec6_getpolicybyaddr(m, dir, IP_FORWARDING, &error); else sp = ipsec6_getpolicybysock(m, dir, in6p->in6p_socket, &error); if (sp == NULL) return 0; size = ipsec_hdrsiz(sp); KEYDEBUG(KEYDEBUG_IPSEC_STAMP, printf("DP ipsec6_hdrsiz call free SP:%p\n", sp)); KEYDEBUG(KEYDEBUG_IPSEC_DATA, printf("ipsec6_hdrsiz: size:%lu.\n", (unsigned long)size)); key_freesp(sp); return size; } #endif /*INET6*/ #ifdef INET /* * encapsulate for ipsec tunnel. * ip->ip_src must be fixed later on. */ static int ipsec4_encapsulate(m, sav) struct mbuf *m; struct secasvar *sav; { struct ip *oip; struct ip *ip; size_t hlen; size_t plen; /* can't tunnel between different AFs */ - if (sav->sah->saidx.src.ss_family != sav->sah->saidx.dst.ss_family - || sav->sah->saidx.src.ss_family != AF_INET) { + if (((struct sockaddr *)&sav->sah->saidx.src)->sa_family + != ((struct sockaddr *)&sav->sah->saidx.dst)->sa_family + || ((struct sockaddr *)&sav->sah->saidx.src)->sa_family != AF_INET) { m_freem(m); return EINVAL; } +#if 0 + /* XXX if the dst is myself, perform nothing. */ + if (key_ismyaddr((struct sockaddr *)&sav->sah->saidx.dst)) { + m_freem(m); + return EINVAL; + } +#endif + if (m->m_len < sizeof(*ip)) + panic("ipsec4_encapsulate: assumption failed (first mbuf length)"); + ip = mtod(m, struct ip *); #ifdef _IP_VHL hlen = _IP_VHL_HL(ip->ip_vhl) << 2; #else hlen = ip->ip_hl << 2; #endif + if (m->m_len != hlen) + panic("ipsec4_encapsulate: assumption failed (first mbuf length)"); + /* generate header checksum */ ip->ip_sum = 0; #ifdef _IP_VHL if (ip->ip_vhl == IP_VHL_BORING) ip->ip_sum = in_cksum_hdr(ip); else ip->ip_sum = in_cksum(m, hlen); #else ip->ip_sum = in_cksum(m, hlen); #endif plen = m->m_pkthdr.len; /* * grow the mbuf to accomodate the new IPv4 header. * NOTE: IPv4 options will never be copied. */ - if (m->m_len != hlen) - panic("ipsec4_encapsulate: assumption failed (first mbuf length)"); if (M_LEADINGSPACE(m->m_next) < hlen) { struct mbuf *n; MGET(n, M_DONTWAIT, MT_DATA); if (!n) { m_freem(m); return ENOBUFS; } n->m_len = hlen; n->m_next = m->m_next; m->m_next = n; m->m_pkthdr.len += hlen; oip = mtod(n, struct ip *); } else { m->m_next->m_len += hlen; m->m_next->m_data -= hlen; m->m_pkthdr.len += hlen; oip = mtod(m->m_next, struct ip *); } ip = mtod(m, struct ip *); ovbcopy((caddr_t)ip, (caddr_t)oip, hlen); m->m_len = sizeof(struct ip); m->m_pkthdr.len -= (hlen - sizeof(struct ip)); /* construct new IPv4 header. see RFC 2401 5.1.2.1 */ /* ECN consideration. */ ip_ecn_ingress(ip4_ipsec_ecn, &ip->ip_tos, &oip->ip_tos); #ifdef _IP_VHL ip->ip_vhl = IP_MAKE_VHL(IPVERSION, sizeof(struct ip) >> 2); #else ip->ip_hl = sizeof(struct ip) >> 2; #endif ip->ip_off &= htons(~IP_OFFMASK); ip->ip_off &= htons(~IP_MF); switch (ip4_ipsec_dfbit) { case 0: /*clear DF bit*/ ip->ip_off &= htons(~IP_DF); break; case 1: /*set DF bit*/ ip->ip_off |= htons(IP_DF); break; default: /*copy DF bit*/ break; } ip->ip_p = IPPROTO_IPIP; if (plen + sizeof(struct ip) < IP_MAXPACKET) ip->ip_len = htons(plen + sizeof(struct ip)); else { - printf("IPv4 ipsec: size exceeds limit: " - "leave ip_len as is (invalid packet)\n"); + ipseclog((LOG_ERR, "IPv4 ipsec: size exceeds limit: " + "leave ip_len as is (invalid packet)\n")); } ip->ip_id = htons(ip_id++); bcopy(&((struct sockaddr_in *)&sav->sah->saidx.src)->sin_addr, &ip->ip_src, sizeof(ip->ip_src)); bcopy(&((struct sockaddr_in *)&sav->sah->saidx.dst)->sin_addr, &ip->ip_dst, sizeof(ip->ip_dst)); /* XXX Should ip_src be updated later ? */ return 0; } #endif /*INET*/ #ifdef INET6 static int ipsec6_encapsulate(m, sav) struct mbuf *m; struct secasvar *sav; { struct ip6_hdr *oip6; struct ip6_hdr *ip6; size_t plen; /* can't tunnel between different AFs */ - if (sav->sah->saidx.src.ss_family != sav->sah->saidx.dst.ss_family - || sav->sah->saidx.src.ss_family != AF_INET6) { + if (((struct sockaddr *)&sav->sah->saidx.src)->sa_family + != ((struct sockaddr *)&sav->sah->saidx.dst)->sa_family + || ((struct sockaddr *)&sav->sah->saidx.src)->sa_family != AF_INET6) { m_freem(m); return EINVAL; } +#if 0 + /* XXX if the dst is myself, perform nothing. */ + if (key_ismyaddr((struct sockaddr *)&sav->sah->saidx.dst)) { + m_freem(m); + return EINVAL; + } +#endif plen = m->m_pkthdr.len; /* * grow the mbuf to accomodate the new IPv6 header. */ if (m->m_len != sizeof(struct ip6_hdr)) panic("ipsec6_encapsulate: assumption failed (first mbuf length)"); if (M_LEADINGSPACE(m->m_next) < sizeof(struct ip6_hdr)) { struct mbuf *n; MGET(n, M_DONTWAIT, MT_DATA); if (!n) { m_freem(m); return ENOBUFS; } n->m_len = sizeof(struct ip6_hdr); n->m_next = m->m_next; m->m_next = n; m->m_pkthdr.len += sizeof(struct ip6_hdr); oip6 = mtod(n, struct ip6_hdr *); } else { m->m_next->m_len += sizeof(struct ip6_hdr); m->m_next->m_data -= sizeof(struct ip6_hdr); m->m_pkthdr.len += sizeof(struct ip6_hdr); oip6 = mtod(m->m_next, struct ip6_hdr *); } ip6 = mtod(m, struct ip6_hdr *); ovbcopy((caddr_t)ip6, (caddr_t)oip6, sizeof(struct ip6_hdr)); /* Fake link-local scope-class addresses */ if (IN6_IS_SCOPE_LINKLOCAL(&oip6->ip6_src)) oip6->ip6_src.s6_addr16[1] = 0; if (IN6_IS_SCOPE_LINKLOCAL(&oip6->ip6_dst)) oip6->ip6_dst.s6_addr16[1] = 0; /* construct new IPv6 header. see RFC 2401 5.1.2.2 */ /* ECN consideration. */ ip6_ecn_ingress(ip6_ipsec_ecn, &ip6->ip6_flow, &oip6->ip6_flow); if (plen < IPV6_MAXPACKET - sizeof(struct ip6_hdr)) ip6->ip6_plen = htons(plen); else { /* ip6->ip6_plen will be updated in ip6_output() */ } ip6->ip6_nxt = IPPROTO_IPV6; bcopy(&((struct sockaddr_in6 *)&sav->sah->saidx.src)->sin6_addr, &ip6->ip6_src, sizeof(ip6->ip6_src)); bcopy(&((struct sockaddr_in6 *)&sav->sah->saidx.dst)->sin6_addr, &ip6->ip6_dst, sizeof(ip6->ip6_dst)); /* XXX Should ip6_src be updated later ? */ return 0; } #endif /*INET6*/ /* * Check the variable replay window. * ipsec_chkreplay() performs replay check before ICV verification. * ipsec_updatereplay() updates replay bitmap. This must be called after * ICV verification (it also performs replay check, which is usually done * beforehand). * 0 (zero) is returned if packet disallowed, 1 if packet permitted. * * based on RFC 2401. */ int ipsec_chkreplay(seq, sav) u_int32_t seq; struct secasvar *sav; { const struct secreplay *replay; u_int32_t diff; int fr; u_int32_t wsizeb; /* constant: bits of window size */ int frlast; /* constant: last frame */ /* sanity check */ if (sav == NULL) - printf("ipsec_chkreplay: NULL pointer was passed.\n"); + panic("ipsec_chkreplay: NULL pointer was passed.\n"); replay = sav->replay; if (replay->wsize == 0) return 1; /* no need to check replay. */ /* constant */ frlast = replay->wsize - 1; wsizeb = replay->wsize << 3; /* sequence number of 0 is invalid */ if (seq == 0) return 0; /* first time is always okay */ if (replay->count == 0) return 1; if (seq > replay->lastseq) { /* larger sequences are okay */ return 1; } else { /* seq is equal or less than lastseq. */ diff = replay->lastseq - seq; /* over range to check, i.e. too old or wrapped */ if (diff >= wsizeb) return 0; fr = frlast - diff / 8; /* this packet already seen ? */ if ((replay->bitmap)[fr] & (1 << (diff % 8))) return 0; /* out of order but good */ return 1; } } +/* + * check replay counter whether to update or not. + * OUT: 0: OK + * 1: NG + */ int ipsec_updatereplay(seq, sav) u_int32_t seq; struct secasvar *sav; { struct secreplay *replay; u_int32_t diff; int fr; u_int32_t wsizeb; /* constant: bits of window size */ int frlast; /* constant: last frame */ /* sanity check */ if (sav == NULL) - printf("ipsec_chkreplay: NULL pointer was passed.\n"); + panic("ipsec_chkreplay: NULL pointer was passed.\n"); replay = sav->replay; if (replay->wsize == 0) goto ok; /* no need to check replay. */ /* constant */ frlast = replay->wsize - 1; wsizeb = replay->wsize << 3; /* sequence number of 0 is invalid */ if (seq == 0) - return 0; + return 1; /* first time */ if (replay->count == 0) { replay->lastseq = seq; bzero(replay->bitmap, replay->wsize); (replay->bitmap)[frlast] = 1; goto ok; } if (seq > replay->lastseq) { /* seq is larger than lastseq. */ diff = seq - replay->lastseq; /* new larger sequence number */ if (diff < wsizeb) { /* In window */ /* set bit for this packet */ vshiftl(replay->bitmap, diff, replay->wsize); (replay->bitmap)[frlast] |= 1; } else { /* this packet has a "way larger" */ bzero(replay->bitmap, replay->wsize); (replay->bitmap)[frlast] = 1; } replay->lastseq = seq; /* larger is good */ } else { /* seq is equal or less than lastseq. */ diff = replay->lastseq - seq; /* over range to check, i.e. too old or wrapped */ if (diff >= wsizeb) - return 0; + return 1; fr = frlast - diff / 8; /* this packet already seen ? */ if ((replay->bitmap)[fr] & (1 << (diff % 8))) - return 0; + return 1; /* mark as seen */ (replay->bitmap)[fr] |= (1 << (diff % 8)); /* out of order but good */ } ok: - if (replay->count == ~0 - && (sav->flags & SADB_X_EXT_CYCSEQ) == 0) { - return 1; /* don't increment, no more packets accepted */ + if (replay->count == ~0) { + + /* set overflow flag */ + replay->overflow++; + + /* don't increment, no more packets accepted */ + if ((sav->flags & SADB_X_EXT_CYCSEQ) == 0) + return 1; + + ipseclog((LOG_WARNING, "replay counter made %d cycle. %s\n", + replay->overflow, ipsec_logsastr(sav))); } replay->count++; - return 1; + return 0; } /* * shift variable length bunffer to left. * IN: bitmap: pointer to the buffer * nbit: the number of to shift. * wsize: buffer size (bytes). */ static void vshiftl(bitmap, nbit, wsize) unsigned char *bitmap; int nbit, wsize; { int s, j, i; unsigned char over; for (j = 0; j < nbit; j += 8) { s = (nbit - j < 8) ? (nbit - j): 8; bitmap[0] <<= s; for (i = 1; i < wsize; i++) { over = (bitmap[i] >> (8 - s)); bitmap[i] <<= s; bitmap[i-1] |= over; } } return; } const char * ipsec4_logpacketstr(ip, spi) struct ip *ip; u_int32_t spi; { static char buf[256]; char *p; u_int8_t *s, *d; s = (u_int8_t *)(&ip->ip_src); d = (u_int8_t *)(&ip->ip_dst); + p = buf; snprintf(buf, sizeof(buf), "packet(SPI=%u ", (u_int32_t)ntohl(spi)); - for (p = buf; p && *p; p++) - ; + while (p && *p) + p++; snprintf(p, sizeof(buf) - (p - buf), "src=%d.%d.%d.%d", s[0], s[1], s[2], s[3]); - for (/*nothing*/; p && *p; p++) - ; + while (p && *p) + p++; snprintf(p, sizeof(buf) - (p - buf), " dst=%d.%d.%d.%d", d[0], d[1], d[2], d[3]); - for (/*nothing*/; p && *p; p++) - ; + while (p && *p) + p++; snprintf(p, sizeof(buf) - (p - buf), ")"); return buf; } #ifdef INET6 const char * ipsec6_logpacketstr(ip6, spi) struct ip6_hdr *ip6; u_int32_t spi; { static char buf[256]; char *p; + p = buf; snprintf(buf, sizeof(buf), "packet(SPI=%u ", (u_int32_t)ntohl(spi)); - for (p = buf; p && *p; p++) - ; + while (p && *p) + p++; snprintf(p, sizeof(buf) - (p - buf), "src=%s", ip6_sprintf(&ip6->ip6_src)); - for (/*nothing*/; p && *p; p++) - ; + while (p && *p) + p++; snprintf(p, sizeof(buf) - (p - buf), " dst=%s", ip6_sprintf(&ip6->ip6_dst)); - for (/*nothing*/; p && *p; p++) - ; + while (p && *p) + p++; snprintf(p, sizeof(buf) - (p - buf), ")"); return buf; } #endif /*INET6*/ const char * ipsec_logsastr(sav) struct secasvar *sav; { static char buf[256]; char *p; struct secasindex *saidx = &sav->sah->saidx; /* validity check */ - if (sav->sah->saidx.src.ss_family != sav->sah->saidx.dst.ss_family) + if (((struct sockaddr *)&sav->sah->saidx.src)->sa_family + != ((struct sockaddr *)&sav->sah->saidx.dst)->sa_family) panic("ipsec_logsastr: family mismatched.\n"); + p = buf; snprintf(buf, sizeof(buf), "SA(SPI=%u ", (u_int32_t)ntohl(sav->spi)); - for (p = buf; p && *p; p++) - ; - if (saidx->src.ss_family == AF_INET) { + while (p && *p) + p++; + if (((struct sockaddr *)&saidx->src)->sa_family == AF_INET) { u_int8_t *s, *d; s = (u_int8_t *)&((struct sockaddr_in *)&saidx->src)->sin_addr; d = (u_int8_t *)&((struct sockaddr_in *)&saidx->dst)->sin_addr; snprintf(p, sizeof(buf) - (p - buf), "src=%d.%d.%d.%d dst=%d.%d.%d.%d", s[0], s[1], s[2], s[3], d[0], d[1], d[2], d[3]); } #ifdef INET6 - else if (saidx->src.ss_family == AF_INET6) { + else if (((struct sockaddr *)&saidx->src)->sa_family == AF_INET6) { snprintf(p, sizeof(buf) - (p - buf), "src=%s", ip6_sprintf(&((struct sockaddr_in6 *)&saidx->src)->sin6_addr)); - for (/*nothing*/; p && *p; p++) - ; + while (p && *p) + p++; snprintf(p, sizeof(buf) - (p - buf), " dst=%s", ip6_sprintf(&((struct sockaddr_in6 *)&saidx->dst)->sin6_addr)); } #endif - for (/*nothing*/; p && *p; p++) - ; + while (p && *p) + p++; snprintf(p, sizeof(buf) - (p - buf), ")"); return buf; } void ipsec_dumpmbuf(m) struct mbuf *m; { int totlen; int i; u_char *p; totlen = 0; printf("---\n"); while (m) { p = mtod(m, u_char *); for (i = 0; i < m->m_len; i++) { printf("%02x ", p[i]); totlen++; if (totlen % 16 == 0) printf("\n"); } m = m->m_next; } if (totlen % 16 != 0) printf("\n"); printf("---\n"); } /* * IPsec output logic for IPv4. */ int ipsec4_output(state, sp, flags) struct ipsec_output_state *state; struct secpolicy *sp; int flags; { struct ip *ip = NULL; struct ipsecrequest *isr = NULL; + struct secasindex saidx; int s; int error; #ifdef IPSEC_SRCSEL struct in_ifaddr *ia; #endif struct sockaddr_in *dst4; + struct sockaddr_in *sin; if (!state) panic("state == NULL in ipsec4_output"); if (!state->m) panic("state->m == NULL in ipsec4_output"); if (!state->ro) panic("state->ro == NULL in ipsec4_output"); if (!state->dst) panic("state->dst == NULL in ipsec4_output"); - ip = mtod(state->m, struct ip *); - KEYDEBUG(KEYDEBUG_IPSEC_DATA, printf("ipsec4_output: applyed SP\n"); kdebug_secpolicy(sp)); for (isr = sp->req; isr != NULL; isr = isr->next) { - if ((error = key_checkrequest(isr)) != 0) { +#if 0 /* give up to check restriction of transport mode */ + /* XXX but should be checked somewhere */ + /* + * some of the IPsec operation must be performed only in + * originating case. + */ + if (isr->saidx.mode == IPSEC_MODE_TRANSPORT + && (flags & IP_FORWARDING)) + continue; +#endif + + /* make SA index for search proper SA */ + ip = mtod(state->m, struct ip *); + bcopy(&isr->saidx, &saidx, sizeof(saidx)); + sin = (struct sockaddr_in *)&saidx.src; + if (sin->sin_len == 0) { + sin->sin_len = sizeof(*sin); + sin->sin_family = AF_INET; + sin->sin_port = IPSEC_PORT_ANY; + bcopy(&ip->ip_src, &sin->sin_addr, + sizeof(sin->sin_addr)); + } + sin = (struct sockaddr_in *)&saidx.dst; + if (sin->sin_len == 0) { + sin->sin_len = sizeof(*sin); + sin->sin_family = AF_INET; + sin->sin_port = IPSEC_PORT_ANY; + bcopy(&ip->ip_dst, &sin->sin_addr, + sizeof(sin->sin_addr)); + } + + if ((error = key_checkrequest(isr, &saidx)) != 0) { /* * IPsec processing is required, but no SA found. * I assume that key_acquire() had been called * to get/establish the SA. Here I discard * this packet because it is responsibility for * upper layer to retransmit the packet. */ ipsecstat.out_nosa++; goto bad; } /* validity check */ if (isr->sav == NULL) { switch (ipsec_get_reqlevel(isr)) { case IPSEC_LEVEL_USE: continue; case IPSEC_LEVEL_REQUIRE: /* must be not reached here. */ panic("ipsec4_output: no SA found, but required."); } } + /* + * If there is no valid SA, we give up to process any + * more. In such a case, the SA's status is changed + * from DYING to DEAD after allocating. If a packet + * send to the receiver by dead SA, the receiver can + * not decode a packet because SA has been dead. + */ if (isr->sav->state != SADB_SASTATE_MATURE && isr->sav->state != SADB_SASTATE_DYING) { - /* If there is no valid SA, we give up to process. */ ipsecstat.out_nosa++; error = EINVAL; goto bad; } /* * There may be the case that SA status will be changed when * we are refering to one. So calling splsoftnet(). */ s = splnet(); if (isr->saidx.mode == IPSEC_MODE_TUNNEL) { /* * build IPsec tunnel. */ /* XXX should be processed with other familiy */ - if (isr->sav->sah->saidx.src.ss_family != AF_INET) { - printf("ipsec4_output: family mismatched " - "between inner and outer spi=%u\n", - (u_int32_t)ntohl(isr->sav->spi)); + if (((struct sockaddr *)&isr->sav->sah->saidx.src)->sa_family != AF_INET) { + ipseclog((LOG_ERR, "ipsec4_output: " + "family mismatched between inner and outer spi=%u\n", + (u_int32_t)ntohl(isr->sav->spi))); splx(s); error = EAFNOSUPPORT; goto bad; } - ip = mtod(state->m, struct ip *); - state->m = ipsec4_splithdr(state->m); if (!state->m) { splx(s); error = ENOMEM; goto bad; } error = ipsec4_encapsulate(state->m, isr->sav); splx(s); if (error) { state->m = NULL; goto bad; } ip = mtod(state->m, struct ip *); state->ro = &isr->sav->sah->sa_route; state->dst = (struct sockaddr *)&state->ro->ro_dst; dst4 = (struct sockaddr_in *)state->dst; if (state->ro->ro_rt && ((state->ro->ro_rt->rt_flags & RTF_UP) == 0 || dst4->sin_addr.s_addr != ip->ip_dst.s_addr)) { RTFREE(state->ro->ro_rt); bzero((caddr_t)state->ro, sizeof (*state->ro)); } if (state->ro->ro_rt == 0) { dst4->sin_family = AF_INET; dst4->sin_len = sizeof(*dst4); dst4->sin_addr = ip->ip_dst; rtalloc(state->ro); } if (state->ro->ro_rt == 0) { ipstat.ips_noroute++; error = EHOSTUNREACH; goto bad; } #ifdef IPSEC_SRCSEL /* * Which address in SA or in routing table should I * select from ? But I had set from SA at * ipsec4_encapsulate(). */ ia = (struct in_ifaddr *)(state->ro->ro_rt->rt_ifa); if (state->ro->ro_rt->rt_flags & RTF_GATEWAY) { state->dst = (struct sockaddr *)state->ro->ro_rt->rt_gateway; dst4 = (struct sockaddr_in *)state->dst; } ip->ip_src = IA_SIN(ia)->sin_addr; #endif } else splx(s); state->m = ipsec4_splithdr(state->m); if (!state->m) { error = ENOMEM; goto bad; } switch (isr->saidx.proto) { case IPPROTO_ESP: #ifdef IPSEC_ESP if ((error = esp4_output(state->m, isr)) != 0) { state->m = NULL; goto bad; } break; #else m_freem(state->m); state->m = NULL; error = EINVAL; goto bad; #endif case IPPROTO_AH: if ((error = ah4_output(state->m, isr)) != 0) { state->m = NULL; goto bad; } break; + case IPPROTO_IPCOMP: + if ((error = ipcomp4_output(state->m, isr)) != 0) { + state->m = NULL; + goto bad; + } + break; default: - printf("ipsec4_output: unknown ipsec protocol %d\n", - isr->saidx.proto); + ipseclog((LOG_ERR, + "ipsec4_output: unknown ipsec protocol %d\n", + isr->saidx.proto)); m_freem(state->m); state->m = NULL; error = EINVAL; goto bad; } if (state->m == 0) { error = ENOMEM; goto bad; } ip = mtod(state->m, struct ip *); } return 0; bad: m_freem(state->m); state->m = NULL; return error; } #ifdef INET6 /* * IPsec output logic for IPv6, transport mode. */ int ipsec6_output_trans(state, nexthdrp, mprev, sp, flags, tun) struct ipsec_output_state *state; u_char *nexthdrp; struct mbuf *mprev; struct secpolicy *sp; int flags; int *tun; { struct ip6_hdr *ip6; struct ipsecrequest *isr = NULL; + struct secasindex saidx; int error = 0; int plen; + struct sockaddr_in6 *sin6; if (!state) panic("state == NULL in ipsec6_output"); if (!state->m) panic("state->m == NULL in ipsec6_output"); if (!nexthdrp) panic("nexthdrp == NULL in ipsec6_output"); if (!mprev) panic("mprev == NULL in ipsec6_output"); if (!sp) panic("sp == NULL in ipsec6_output"); if (!tun) panic("tun == NULL in ipsec6_output"); KEYDEBUG(KEYDEBUG_IPSEC_DATA, printf("ipsec6_output_trans: applyed SP\n"); kdebug_secpolicy(sp)); *tun = 0; for (isr = sp->req; isr; isr = isr->next) { if (isr->saidx.mode == IPSEC_MODE_TUNNEL) { /* the rest will be handled by ipsec6_output_tunnel() */ break; } - if (key_checkrequest(isr) == ENOENT) { + /* make SA index for search proper SA */ + ip6 = mtod(state->m, struct ip6_hdr *); + bcopy(&isr->saidx, &saidx, sizeof(saidx)); + sin6 = (struct sockaddr_in6 *)&saidx.src; + if (sin6->sin6_len == 0) { + sin6->sin6_len = sizeof(*sin6); + sin6->sin6_family = AF_INET6; + sin6->sin6_port = IPSEC_PORT_ANY; + bcopy(&ip6->ip6_src, &sin6->sin6_addr, + sizeof(ip6->ip6_src)); + if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) { + /* fix scope id for comparing SPD */ + sin6->sin6_addr.s6_addr16[1] = 0; + sin6->sin6_scope_id = ntohs(ip6->ip6_src.s6_addr16[1]); + } + } + sin6 = (struct sockaddr_in6 *)&saidx.dst; + if (sin6->sin6_len == 0) { + sin6->sin6_len = sizeof(*sin6); + sin6->sin6_family = AF_INET6; + sin6->sin6_port = IPSEC_PORT_ANY; + bcopy(&ip6->ip6_dst, &sin6->sin6_addr, + sizeof(ip6->ip6_dst)); + if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) { + /* fix scope id for comparing SPD */ + sin6->sin6_addr.s6_addr16[1] = 0; + sin6->sin6_scope_id = ntohs(ip6->ip6_dst.s6_addr16[1]); + } + } + + if (key_checkrequest(isr, &saidx) == ENOENT) { /* * IPsec processing is required, but no SA found. * I assume that key_acquire() had been called * to get/establish the SA. Here I discard * this packet because it is responsibility for * upper layer to retransmit the packet. */ ipsec6stat.out_nosa++; error = ENOENT; goto bad; } /* validity check */ if (isr->sav == NULL) { switch (ipsec_get_reqlevel(isr)) { case IPSEC_LEVEL_USE: continue; case IPSEC_LEVEL_REQUIRE: /* must be not reached here. */ panic("ipsec6_output_trans: no SA found, but required."); } } + /* + * If there is no valid SA, we give up to process. + * see same place at ipsec4_output(). + */ if (isr->sav->state != SADB_SASTATE_MATURE && isr->sav->state != SADB_SASTATE_DYING) { - /* If there is no valid SA, we give up to process. */ ipsec6stat.out_nosa++; error = EINVAL; goto bad; } switch (isr->saidx.proto) { case IPPROTO_ESP: #ifdef IPSEC_ESP error = esp6_output(state->m, nexthdrp, mprev->m_next, isr); #else m_freem(state->m); error = EINVAL; #endif break; case IPPROTO_AH: error = ah6_output(state->m, nexthdrp, mprev->m_next, isr); break; + case IPPROTO_IPCOMP: + error = ipcomp6_output(state->m, nexthdrp, mprev->m_next, isr); + break; default: - printf("ipsec6_output_trans: unknown ipsec protocol %d\n", isr->saidx.proto); + ipseclog((LOG_ERR, "ipsec6_output_trans: " + "unknown ipsec protocol %d\n", isr->saidx.proto)); m_freem(state->m); + ipsec6stat.out_inval++; error = EINVAL; break; } if (error) { state->m = NULL; goto bad; } plen = state->m->m_pkthdr.len - sizeof(struct ip6_hdr); if (plen > IPV6_MAXPACKET) { - printf("ipsec6_output: IPsec with IPv6 jumbogram is not supported\n"); + ipseclog((LOG_ERR, "ipsec6_output_trans: " + "IPsec with IPv6 jumbogram is not supported\n")); ipsec6stat.out_inval++; error = EINVAL; /*XXX*/ goto bad; } ip6 = mtod(state->m, struct ip6_hdr *); ip6->ip6_plen = htons(plen); } /* if we have more to go, we need a tunnel mode processing */ if (isr != NULL) *tun = 1; return 0; bad: m_freem(state->m); state->m = NULL; return error; } /* * IPsec output logic for IPv6, tunnel mode. */ int ipsec6_output_tunnel(state, sp, flags) struct ipsec_output_state *state; struct secpolicy *sp; int flags; { struct ip6_hdr *ip6; struct ipsecrequest *isr = NULL; + struct secasindex saidx; int error = 0; int plen; #ifdef IPSEC_SRCSEL struct in6_addr *ia6; #endif struct sockaddr_in6* dst6; int s; if (!state) panic("state == NULL in ipsec6_output"); if (!state->m) panic("state->m == NULL in ipsec6_output"); if (!sp) panic("sp == NULL in ipsec6_output"); KEYDEBUG(KEYDEBUG_IPSEC_DATA, printf("ipsec6_output_tunnel: applyed SP\n"); kdebug_secpolicy(sp)); /* * transport mode ipsec (before the 1st tunnel mode) is already * processed by ipsec6_output_trans(). */ for (isr = sp->req; isr; isr = isr->next) { if (isr->saidx.mode == IPSEC_MODE_TUNNEL) break; } for (/*already initialized*/; isr; isr = isr->next) { - if (key_checkrequest(isr) == ENOENT) { + /* When tunnel mode, SA peers must be specified. */ + bcopy(&isr->saidx, &saidx, sizeof(saidx)); + if (key_checkrequest(isr, &saidx) == ENOENT) { /* * IPsec processing is required, but no SA found. * I assume that key_acquire() had been called * to get/establish the SA. Here I discard * this packet because it is responsibility for * upper layer to retransmit the packet. */ ipsec6stat.out_nosa++; error = ENOENT; goto bad; } /* validity check */ if (isr->sav == NULL) { switch (ipsec_get_reqlevel(isr)) { case IPSEC_LEVEL_USE: continue; case IPSEC_LEVEL_REQUIRE: /* must be not reached here. */ panic("ipsec6_output_tunnel: no SA found, but required."); } } + /* + * If there is no valid SA, we give up to process. + * see same place at ipsec4_output(). + */ if (isr->sav->state != SADB_SASTATE_MATURE && isr->sav->state != SADB_SASTATE_DYING) { - /* If there is no valid SA, we give up to process. */ ipsec6stat.out_nosa++; error = EINVAL; goto bad; } /* * There may be the case that SA status will be changed when * we are refering to one. So calling splsoftnet(). */ s = splnet(); if (isr->saidx.mode == IPSEC_MODE_TUNNEL) { /* * build IPsec tunnel. */ /* XXX should be processed with other familiy */ - if (isr->sav->sah->saidx.src.ss_family != AF_INET6) { - printf("ipsec4_output: family mismatched " - "between inner and outer, spi=%u\n", - (u_int32_t)ntohl(isr->sav->spi)); - printf("ipsec6_output_tunnel: family mismatched " - "between inner and outer, spi=%u\n", - (u_int32_t)ntohl(isr->sav->spi)); + if (((struct sockaddr *)&isr->sav->sah->saidx.src)->sa_family != AF_INET6) { + ipseclog((LOG_ERR, "ipsec6_output_tunnel: " + "family mismatched between inner and outer, spi=%u\n", + (u_int32_t)ntohl(isr->sav->spi))); splx(s); + ipsec6stat.out_inval++; error = EAFNOSUPPORT; goto bad; } - ip6 = mtod(state->m, struct ip6_hdr *); - state->m = ipsec6_splithdr(state->m); if (!state->m) { splx(s); + ipsec6stat.out_nomem++; error = ENOMEM; goto bad; } error = ipsec6_encapsulate(state->m, isr->sav); splx(s); if (error) { state->m = 0; goto bad; } ip6 = mtod(state->m, struct ip6_hdr *); state->ro = &isr->sav->sah->sa_route; state->dst = (struct sockaddr *)&state->ro->ro_dst; dst6 = (struct sockaddr_in6 *)state->dst; if (state->ro->ro_rt && ((state->ro->ro_rt->rt_flags & RTF_UP) == 0 || !IN6_ARE_ADDR_EQUAL(&dst6->sin6_addr, &ip6->ip6_dst))) { RTFREE(state->ro->ro_rt); bzero((caddr_t)state->ro, sizeof (*state->ro)); } if (state->ro->ro_rt == 0) { bzero(dst6, sizeof(*dst6)); dst6->sin6_family = AF_INET6; dst6->sin6_len = sizeof(*dst6); dst6->sin6_addr = ip6->ip6_dst; rtalloc(state->ro); } if (state->ro->ro_rt == 0) { ip6stat.ip6s_noroute++; + ipsec6stat.out_noroute++; error = EHOSTUNREACH; goto bad; } +#if 0 /* XXX Is the following need ? */ + if (state->ro->ro_rt->rt_flags & RTF_GATEWAY) { + state->dst = (struct sockaddr *)state->ro->ro_rt->rt_gateway; + dst6 = (struct sockaddr_in6 *)state->dst; + } +#endif #ifdef IPSEC_SRCSEL /* * Which address in SA or in routing table should I * select from ? But I had set from SA at * ipsec6_encapsulate(). */ ia6 = in6_selectsrc(dst6, NULL, NULL, (struct route_in6 *)state->ro, NULL, &error); - if (ia6 == NULL) + if (ia6 == NULL) { + ip6stat.ip6s_noroute++; + ipsec6stat.out_noroute++; goto bad; + } ip6->ip6_src = *ia6; #endif } else splx(s); state->m = ipsec6_splithdr(state->m); if (!state->m) { + ipsec6stat.out_nomem++; error = ENOMEM; goto bad; } ip6 = mtod(state->m, struct ip6_hdr *); switch (isr->saidx.proto) { case IPPROTO_ESP: #ifdef IPSEC_ESP error = esp6_output(state->m, &ip6->ip6_nxt, state->m->m_next, isr); #else m_freem(state->m); error = EINVAL; #endif break; case IPPROTO_AH: error = ah6_output(state->m, &ip6->ip6_nxt, state->m->m_next, isr); break; + case IPPROTO_IPCOMP: + /* XXX code should be here */ + /*FALLTHROUGH*/ default: - printf("ipsec6_output_tunnel: unknown ipsec protocol %d\n", isr->saidx.proto); + ipseclog((LOG_ERR, "ipsec6_output_tunnel: " + "unknown ipsec protocol %d\n", isr->saidx.proto)); m_freem(state->m); + ipsec6stat.out_inval++; error = EINVAL; break; } if (error) { state->m = NULL; goto bad; } plen = state->m->m_pkthdr.len - sizeof(struct ip6_hdr); if (plen > IPV6_MAXPACKET) { - printf("ipsec6_output_tunnel: IPsec with IPv6 jumbogram is not supported\n"); + ipseclog((LOG_ERR, "ipsec6_output_tunnel: " + "IPsec with IPv6 jumbogram is not supported\n")); ipsec6stat.out_inval++; error = EINVAL; /*XXX*/ goto bad; } ip6 = mtod(state->m, struct ip6_hdr *); ip6->ip6_plen = htons(plen); } return 0; bad: m_freem(state->m); state->m = NULL; return error; } #endif /*INET6*/ /* * Chop IP header and option off from the payload. */ static struct mbuf * ipsec4_splithdr(m) struct mbuf *m; { struct mbuf *mh; struct ip *ip; int hlen; if (m->m_len < sizeof(struct ip)) panic("ipsec4_splithdr: first mbuf too short"); ip = mtod(m, struct ip *); #ifdef _IP_VHL hlen = _IP_VHL_HL(ip->ip_vhl) << 2; #else hlen = ip->ip_hl << 2; #endif if (m->m_len > hlen) { MGETHDR(mh, M_DONTWAIT, MT_HEADER); if (!mh) { m_freem(m); return NULL; } M_COPY_PKTHDR(mh, m); MH_ALIGN(mh, hlen); m->m_flags &= ~M_PKTHDR; m->m_len -= hlen; m->m_data += hlen; mh->m_next = m; m = mh; m->m_len = hlen; bcopy((caddr_t)ip, mtod(m, caddr_t), hlen); } else if (m->m_len < hlen) { m = m_pullup(m, hlen); if (!m) return NULL; } return m; } #ifdef INET6 static struct mbuf * ipsec6_splithdr(m) struct mbuf *m; { struct mbuf *mh; struct ip6_hdr *ip6; int hlen; if (m->m_len < sizeof(struct ip6_hdr)) panic("ipsec6_splithdr: first mbuf too short"); ip6 = mtod(m, struct ip6_hdr *); hlen = sizeof(struct ip6_hdr); if (m->m_len > hlen) { MGETHDR(mh, M_DONTWAIT, MT_HEADER); if (!mh) { m_freem(m); return NULL; } M_COPY_PKTHDR(mh, m); MH_ALIGN(mh, hlen); m->m_flags &= ~M_PKTHDR; m->m_len -= hlen; m->m_data += hlen; mh->m_next = m; m = mh; m->m_len = hlen; bcopy((caddr_t)ip6, mtod(m, caddr_t), hlen); } else if (m->m_len < hlen) { m = m_pullup(m, hlen); if (!m) return NULL; } return m; } #endif /* validate inbound IPsec tunnel packet. */ int ipsec4_tunnel_validate(ip, nxt0, sav) struct ip *ip; u_int nxt0; struct secasvar *sav; { u_int8_t nxt = nxt0 & 0xff; struct sockaddr_in *sin; int hlen; if (nxt != IPPROTO_IPV4) return 0; #ifdef _IP_VHL hlen = _IP_VHL_HL(ip->ip_vhl) << 2; #else hlen = ip->ip_hl << 2; #endif if (hlen != sizeof(struct ip)) return 0; - switch (sav->sah->saidx.dst.ss_family) { + switch (((struct sockaddr *)&sav->sah->saidx.dst)->sa_family) { case AF_INET: sin = (struct sockaddr_in *)&sav->sah->saidx.dst; if (bcmp(&ip->ip_dst, &sin->sin_addr, sizeof(ip->ip_dst)) != 0) return 0; break; #ifdef INET6 case AF_INET6: /* should be supported, but at this moment we don't. */ /*FALLTHROUGH*/ #endif default: return 0; } return 1; } #ifdef INET6 /* validate inbound IPsec tunnel packet. */ int ipsec6_tunnel_validate(ip6, nxt0, sav) struct ip6_hdr *ip6; u_int nxt0; struct secasvar *sav; { u_int8_t nxt = nxt0 & 0xff; struct sockaddr_in6 *sin6; if (nxt != IPPROTO_IPV6) return 0; - switch (sav->sah->saidx.dst.ss_family) { + switch (((struct sockaddr *)&sav->sah->saidx.dst)->sa_family) { case AF_INET6: sin6 = ((struct sockaddr_in6 *)&sav->sah->saidx.dst); if (!IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &sin6->sin6_addr)) return 0; break; case AF_INET: /* should be supported, but at this moment we don't. */ /*FALLTHROUGH*/ default: return 0; } return 1; } #endif /* * Make a mbuf chain for encryption. * If the original mbuf chain contains a mbuf with a cluster, * allocate a new cluster and copy the data to the new cluster. * XXX: this hack is inefficient, but is necessary to handle cases * of TCP retransmission... */ struct mbuf * ipsec_copypkt(m) struct mbuf *m; { struct mbuf *n, **mpp, *mnew; for (n = m, mpp = &m; n; n = n->m_next) { if (n->m_flags & M_EXT) { /* * Make a copy only if there are more than one references * to the cluster. * XXX: is this approach effective? */ if ( n->m_ext.ext_free || mclrefcnt[mtocl(n->m_ext.ext_buf)] > 1 ) { int remain, copied; struct mbuf *mm; if (n->m_flags & M_PKTHDR) { MGETHDR(mnew, M_DONTWAIT, MT_HEADER); if (mnew == NULL) goto fail; mnew->m_pkthdr = n->m_pkthdr; +#if 0 + if (n->m_pkthdr.aux) { + mnew->m_pkthdr.aux = + m_copym(n->m_pkthdr.aux, + 0, M_COPYALL, M_DONTWAIT); + } +#endif + M_COPY_PKTHDR(mnew, n); mnew->m_flags = n->m_flags & M_COPYFLAGS; } else { MGET(mnew, M_DONTWAIT, MT_DATA); if (mnew == NULL) goto fail; } mnew->m_len = 0; mm = mnew; /* * Copy data. If we don't have enough space to * store the whole data, allocate a cluster * or additional mbufs. * XXX: we don't use m_copyback(), since the * function does not use clusters and thus is * inefficient. */ remain = n->m_len; copied = 0; while(1) { int len; struct mbuf *mn; if (remain <= (mm->m_flags & M_PKTHDR ? MHLEN : MLEN)) len = remain; else { /* allocate a cluster */ MCLGET(mm, M_DONTWAIT); if (!(mm->m_flags & M_EXT)) { m_free(mm); goto fail; } len = remain < MCLBYTES ? remain : MCLBYTES; } bcopy(n->m_data + copied, mm->m_data, len); copied += len; remain -= len; mm->m_len = len; if (remain <= 0) /* completed? */ break; /* need another mbuf */ MGETHDR(mn, M_DONTWAIT, MT_HEADER); if (mn == NULL) goto fail; mm->m_next = mn; mm = mn; } /* adjust chain */ mm->m_next = m_free(n); n = mm; *mpp = mnew; mpp = &n->m_next; continue; } } *mpp = n; mpp = &n->m_next; } return(m); fail: m_freem(m); return(NULL); +} + +void +ipsec_setsocket(m, so) + struct mbuf *m; + struct socket *so; +{ + struct mbuf *n; + + n = m_aux_find(m, AF_INET, IPPROTO_ESP); + if (so && !n) + n = m_aux_add(m, AF_INET, IPPROTO_ESP); + if (n) { + if (so) { + *mtod(n, struct socket **) = so; + /* + * XXX think again about it when we put decryption + * histrory into aux mbuf + */ + n->m_len = sizeof(struct socket *); + } else + m_aux_delete(m, n); + } +} + +struct socket * +ipsec_getsocket(m) + struct mbuf *m; +{ + struct mbuf *n; + + n = m_aux_find(m, AF_INET, IPPROTO_ESP); + if (n && n->m_len >= sizeof(struct socket *)) + return *mtod(n, struct socket **); + else + return NULL; } Index: head/sys/netinet6/ipsec.h =================================================================== --- head/sys/netinet6/ipsec.h (revision 62586) +++ head/sys/netinet6/ipsec.h (revision 62587) @@ -1,320 +1,324 @@ +/* $FreeBSD$ */ +/* $KAME: ipsec.h,v 1.33 2000/06/19 14:31:49 sakane Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* * IPsec controller part. */ #ifndef _NETINET6_IPSEC_H_ #define _NETINET6_IPSEC_H_ #include #include #ifdef _KERNEL /* * Security Policy Index - * NOTE: Encure to be same address family and upper layer protocol. + * NOTE: Ensure to be same address family and upper layer protocol. * NOTE: ul_proto, port number, uid, gid: * ANY: reserved for waldcard. * 0 to (~0 - 1): is one of the number of each value. */ struct secpolicyindex { - u_int8_t dir; /* direction of packet flow, see blow */ - struct sockaddr_storage src; /* IP src address for SP */ - struct sockaddr_storage dst; /* IP dst address for SP */ - u_int8_t prefs; /* prefix length in bits for src */ - u_int8_t prefd; /* prefix length in bits for dst */ - u_int16_t ul_proto; /* upper layer Protocol */ + u_int8_t dir; /* direction of packet flow, see blow */ + struct sockaddr_storage src; /* IP src address for SP */ + struct sockaddr_storage dst; /* IP dst address for SP */ + u_int8_t prefs; /* prefix length in bits for src */ + u_int8_t prefd; /* prefix length in bits for dst */ + u_int16_t ul_proto; /* upper layer Protocol */ +#ifdef notyet + uid_t uids; + uid_t uidd; + gid_t gids; + gid_t gidd; +#endif }; /* Security Policy Data Base */ struct secpolicy { LIST_ENTRY(secpolicy) chain; - int refcnt; /* reference count */ + int refcnt; /* reference count */ struct secpolicyindex spidx; /* selector */ - u_int state; /* 0: dead, others: alive */ -#define IPSEC_SPSTATE_DEAD 0 -#define IPSEC_SPSTATE_ALIVE 1 + u_int32_t id; /* It's unique number on the system. */ + u_int state; /* 0: dead, others: alive */ +#define IPSEC_SPSTATE_DEAD 0 +#define IPSEC_SPSTATE_ALIVE 1 - u_int policy; /* DISCARD, NONE or IPSEC, see keyv2.h */ - struct ipsecrequest *req; + u_int policy; /* DISCARD, NONE or IPSEC, see keyv2.h */ + struct ipsecrequest *req; /* pointer to the ipsec request tree, */ /* if policy == IPSEC else this value == NULL.*/ }; /* Request for IPsec */ struct ipsecrequest { - struct ipsecrequest *next; + struct ipsecrequest *next; /* pointer to next structure */ /* If NULL, it means the end of chain. */ - struct secasindex saidx; - u_int level; /* IPsec level defined below. */ + struct secasindex saidx;/* hint for search proper SA */ + /* if __ss_len == 0 then no address specified.*/ + u_int level; /* IPsec level defined below. */ - struct secasvar *sav; /* place holder of SA for use */ - struct secpolicy *sp; /* back pointer to SP */ + struct secasvar *sav; /* place holder of SA for use */ + struct secpolicy *sp; /* back pointer to SP */ }; /* security policy in PCB */ struct inpcbpolicy { - struct secpolicy *sp_in; - struct secpolicy *sp_out; - int priv; /* privileged socket ? */ + struct secpolicy *sp_in; + struct secpolicy *sp_out; + int priv; /* privileged socket ? */ }; + +/* SP acquiring list table. */ +struct secspacq { + LIST_ENTRY(secspacq) chain; + + struct secpolicyindex spidx; + + u_int32_t tick; /* for lifetime */ + int count; /* for lifetime */ + /* XXX: here is mbuf place holder to be sent ? */ +}; #endif /*_KERNEL*/ -#define IPSEC_PORT_ANY 65535 -#define IPSEC_ULPROTO_ANY 255 -#define IPSEC_PROTO_ANY 65535 +/* according to IANA assignment, port 0x0000 and proto 0xff are reserved. */ +#define IPSEC_PORT_ANY 0 +#define IPSEC_ULPROTO_ANY 255 +#define IPSEC_PROTO_ANY 255 /* mode of security protocol */ /* NOTE: DON'T use IPSEC_MODE_ANY at SPD. It's only use in SAD */ #define IPSEC_MODE_ANY 0 /* i.e. wildcard. */ #define IPSEC_MODE_TRANSPORT 1 #define IPSEC_MODE_TUNNEL 2 /* * Direction of security policy. * NOTE: Since INVALID is used just as flag. * The other are used for loop counter too. */ -#define IPSEC_DIR_ANY 0 -#define IPSEC_DIR_INBOUND 1 -#define IPSEC_DIR_OUTBOUND 2 -#define IPSEC_DIR_MAX 3 -#define IPSEC_DIR_INVALID 4 +#define IPSEC_DIR_ANY 0 +#define IPSEC_DIR_INBOUND 1 +#define IPSEC_DIR_OUTBOUND 2 +#define IPSEC_DIR_MAX 3 +#define IPSEC_DIR_INVALID 4 /* Policy level */ /* * IPSEC, ENTRUST and BYPASS are allowd for setsockopt() in PCB, * DISCARD, IPSEC and NONE are allowd for setkey() in SPD. * DISCARD and NONE are allowd for system default. */ -#define IPSEC_POLICY_DISCARD 0 /* discarding packet */ -#define IPSEC_POLICY_NONE 1 /* through IPsec engine */ -#define IPSEC_POLICY_IPSEC 2 /* do IPsec */ -#define IPSEC_POLICY_ENTRUST 3 /* consulting SPD if present. */ -#define IPSEC_POLICY_BYPASS 4 /* only for privileged socket. */ +#define IPSEC_POLICY_DISCARD 0 /* discarding packet */ +#define IPSEC_POLICY_NONE 1 /* through IPsec engine */ +#define IPSEC_POLICY_IPSEC 2 /* do IPsec */ +#define IPSEC_POLICY_ENTRUST 3 /* consulting SPD if present. */ +#define IPSEC_POLICY_BYPASS 4 /* only for privileged socket. */ /* Security protocol level */ #define IPSEC_LEVEL_DEFAULT 0 /* reference to system default */ #define IPSEC_LEVEL_USE 1 /* use SA if present. */ #define IPSEC_LEVEL_REQUIRE 2 /* require SA. */ #define IPSEC_LEVEL_UNIQUE 3 /* unique SA. */ +#define IPSEC_MANUAL_REQID_MAX 0x3fff + /* + * if security policy level == unique, this id + * indicate to a relative SA for use, else is + * zero. + * 1 - 0x3fff are reserved for manual keying. + * 0 are reserved for above reason. Others is + * for kernel use. + * Note that this id doesn't identify SA + * by only itself. + */ #define IPSEC_REPLAYWSIZE 32 /* statistics for ipsec processing */ struct ipsecstat { - u_long in_success; /* succeeded inbound process */ - u_long in_polvio; /* security policy violation for inbound process */ - u_long in_nosa; /* inbound SA is unavailable */ - u_long in_inval; /* inbound processing failed due to EINVAL */ - u_long in_badspi; /* failed getting a SPI */ - u_long in_ahreplay; /* AH replay check failed */ - u_long in_espreplay; /* ESP replay check failed */ - u_long in_ahauthsucc; /* AH authentication success */ - u_long in_ahauthfail; /* AH authentication failure */ - u_long in_espauthsucc; /* ESP authentication success */ - u_long in_espauthfail; /* ESP authentication failure */ - u_long in_esphist[SADB_EALG_MAX]; - u_long in_ahhist[SADB_AALG_MAX]; - u_long out_success; /* succeeded outbound process */ - u_long out_polvio; /* security policy violation for outbound process */ - u_long out_nosa; /* outbound SA is unavailable */ - u_long out_inval; /* outbound process failed due to EINVAL */ - u_long out_noroute; /* there is no route */ - u_long out_esphist[SADB_EALG_MAX]; - u_long out_ahhist[SADB_AALG_MAX]; + u_quad_t in_success; /* succeeded inbound process */ + u_quad_t in_polvio; + /* security policy violation for inbound process */ + u_quad_t in_nosa; /* inbound SA is unavailable */ + u_quad_t in_inval; /* inbound processing failed due to EINVAL */ + u_quad_t in_nomem; /* inbound processing failed due to ENOBUFS */ + u_quad_t in_badspi; /* failed getting a SPI */ + u_quad_t in_ahreplay; /* AH replay check failed */ + u_quad_t in_espreplay; /* ESP replay check failed */ + u_quad_t in_ahauthsucc; /* AH authentication success */ + u_quad_t in_ahauthfail; /* AH authentication failure */ + u_quad_t in_espauthsucc; /* ESP authentication success */ + u_quad_t in_espauthfail; /* ESP authentication failure */ + u_quad_t in_esphist[256]; + u_quad_t in_ahhist[256]; + u_quad_t in_comphist[256]; + u_quad_t out_success; /* succeeded outbound process */ + u_quad_t out_polvio; + /* security policy violation for outbound process */ + u_quad_t out_nosa; /* outbound SA is unavailable */ + u_quad_t out_inval; /* outbound process failed due to EINVAL */ + u_quad_t out_nomem; /* inbound processing failed due to ENOBUFS */ + u_quad_t out_noroute; /* there is no route */ + u_quad_t out_esphist[256]; + u_quad_t out_ahhist[256]; + u_quad_t out_comphist[256]; }; /* * Definitions for IPsec & Key sysctl operations. */ /* * Names for IPsec & Key sysctl objects */ -#define IPSECCTL_STATS 1 /* stats */ -#define IPSECCTL_DEF_POLICY 2 -#define IPSECCTL_DEF_ESP_TRANSLEV 3 /* int; ESP transport mode */ -#define IPSECCTL_DEF_ESP_NETLEV 4 /* int; ESP tunnel mode */ -#define IPSECCTL_DEF_AH_TRANSLEV 5 /* int; AH transport mode */ -#define IPSECCTL_DEF_AH_NETLEV 6 /* int; AH tunnel mode */ -#define IPSECCTL_INBOUND_CALL_IKE 7 +#define IPSECCTL_STATS 1 /* stats */ +#define IPSECCTL_DEF_POLICY 2 +#define IPSECCTL_DEF_ESP_TRANSLEV 3 /* int; ESP transport mode */ +#define IPSECCTL_DEF_ESP_NETLEV 4 /* int; ESP tunnel mode */ +#define IPSECCTL_DEF_AH_TRANSLEV 5 /* int; AH transport mode */ +#define IPSECCTL_DEF_AH_NETLEV 6 /* int; AH tunnel mode */ +#if 0 /*obsolete, do not reuse*/ +#define IPSECCTL_INBOUND_CALL_IKE 7 +#endif #define IPSECCTL_AH_CLEARTOS 8 #define IPSECCTL_AH_OFFSETMASK 9 #define IPSECCTL_DFBIT 10 #define IPSECCTL_ECN 11 -#define IPSECCTL_MAXID 12 +#define IPSECCTL_DEBUG 12 +#define IPSECCTL_MAXID 13 #define IPSECCTL_NAMES { \ { 0, 0 }, \ { 0, 0 }, \ { "def_policy", CTLTYPE_INT }, \ { "esp_trans_deflev", CTLTYPE_INT }, \ { "esp_net_deflev", CTLTYPE_INT }, \ { "ah_trans_deflev", CTLTYPE_INT }, \ { "ah_net_deflev", CTLTYPE_INT }, \ - { "inbound_call_ike", CTLTYPE_INT }, \ + { 0, 0 }, \ { "ah_cleartos", CTLTYPE_INT }, \ { "ah_offsetmask", CTLTYPE_INT }, \ { "dfbit", CTLTYPE_INT }, \ { "ecn", CTLTYPE_INT }, \ + { "debug", CTLTYPE_INT }, \ } #define IPSEC6CTL_NAMES { \ { 0, 0 }, \ { 0, 0 }, \ { "def_policy", CTLTYPE_INT }, \ { "esp_trans_deflev", CTLTYPE_INT }, \ { "esp_net_deflev", CTLTYPE_INT }, \ { "ah_trans_deflev", CTLTYPE_INT }, \ { "ah_net_deflev", CTLTYPE_INT }, \ - { "inbound_call_ike", CTLTYPE_INT }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ + { 0, 0 }, \ { "ecn", CTLTYPE_INT }, \ + { "debug", CTLTYPE_INT }, \ } -#define IPSECCTL_VARS { \ - 0, \ - 0, \ - &ip4_def_policy.policy, \ - &ip4_esp_trans_deflev, \ - &ip4_esp_net_deflev, \ - &ip4_ah_trans_deflev, \ - &ip4_ah_net_deflev, \ - &ip4_inbound_call_ike, \ - &ip4_ah_cleartos, \ - &ip4_ah_offsetmask, \ - &ip4_ipsec_dfbit, \ - &ip4_ipsec_ecn, \ -} - -#define IPSEC6CTL_VARS { \ - 0, \ - 0, \ - &ip6_def_policy.policy, \ - &ip6_esp_trans_deflev, \ - &ip6_esp_net_deflev, \ - &ip6_ah_trans_deflev, \ - &ip6_ah_net_deflev, \ - &ip6_inbound_call_ike, \ - 0, \ - 0, \ - 0, \ - &ip6_ipsec_ecn, \ -} - #ifdef _KERNEL - -#ifdef SYSCTL_DECL -SYSCTL_DECL(_net_inet_ipsec); -#endif - struct ipsec_output_state { - struct mbuf *m; - struct route *ro; - struct sockaddr *dst; + struct mbuf *m; + struct route *ro; + struct sockaddr *dst; }; -extern struct ipsecstat ipsecstat; -extern struct secpolicy ip4_def_policy; -extern int ip4_esp_trans_deflev; -extern int ip4_esp_net_deflev; -extern int ip4_ah_trans_deflev; -extern int ip4_ah_net_deflev; -extern int ip4_inbound_call_ike; -extern int ip4_ah_cleartos; -extern int ip4_ah_offsetmask; -extern int ip4_ipsec_dfbit; -extern int ip4_ipsec_ecn; +extern int ipsec_debug; -extern struct secpolicy *ipsec4_getpolicybysock +extern struct ipsecstat ipsecstat; +extern struct secpolicy ip4_def_policy; +extern int ip4_esp_trans_deflev; +extern int ip4_esp_net_deflev; +extern int ip4_ah_trans_deflev; +extern int ip4_ah_net_deflev; +extern int ip4_ah_cleartos; +extern int ip4_ah_offsetmask; +extern int ip4_ipsec_dfbit; +extern int ip4_ipsec_ecn; + +#define ipseclog(x) do { if (ipsec_debug) log x; } while (0) + +extern struct secpolicy *ipsec4_getpolicybysock __P((struct mbuf *, u_int, struct socket *, int *)); -extern struct secpolicy *ipsec4_getpolicybyaddr +extern struct secpolicy *ipsec4_getpolicybyaddr __P((struct mbuf *, u_int, int, int *)); -struct inpcb; - -extern int ipsec_init_policy __P((struct socket *so, struct inpcbpolicy **)); -extern int ipsec_copy_policy +struct inpcb; +extern int ipsec_init_policy __P((struct socket *so, struct inpcbpolicy **)); +extern int ipsec_copy_policy __P((struct inpcbpolicy *, struct inpcbpolicy *)); -extern u_int ipsec_get_reqlevel __P((struct ipsecrequest *)); +extern u_int ipsec_get_reqlevel __P((struct ipsecrequest *)); -extern int ipsec4_set_policy __P((struct inpcb *inp, int optname, - caddr_t request, int priv)); -extern int ipsec4_get_policy - __P((struct inpcb *inpcb, caddr_t request, struct mbuf **mp)); -extern int ipsec4_delete_pcbpolicy __P((struct inpcb *)); -extern int ipsec4_in_reject_so __P((struct mbuf *, struct socket *)); -extern int ipsec4_in_reject __P((struct mbuf *, struct inpcb *)); +extern int ipsec4_set_policy __P((struct inpcb *inp, int optname, + caddr_t request, size_t len, int priv)); +extern int ipsec4_get_policy __P((struct inpcb *inpcb, caddr_t request, + size_t len, struct mbuf **mp)); +extern int ipsec4_delete_pcbpolicy __P((struct inpcb *)); +extern int ipsec4_in_reject_so __P((struct mbuf *, struct socket *)); +extern int ipsec4_in_reject __P((struct mbuf *, struct inpcb *)); -struct secas; -struct tcpcb; -struct tcp6cb; -extern int ipsec_chkreplay __P((u_int32_t, struct secasvar *)); -extern int ipsec_updatereplay __P((u_int32_t, struct secasvar *)); +struct secas; +struct tcpcb; +extern int ipsec_chkreplay __P((u_int32_t, struct secasvar *)); +extern int ipsec_updatereplay __P((u_int32_t, struct secasvar *)); -extern size_t ipsec4_hdrsiz __P((struct mbuf *, u_int, struct inpcb *)); -extern size_t ipsec_hdrsiz_tcp __P((struct tcpcb *)); +extern size_t ipsec4_hdrsiz __P((struct mbuf *, u_int, struct inpcb *)); +extern size_t ipsec_hdrsiz_tcp __P((struct tcpcb *)); -struct ip; +struct ip; +extern const char *ipsec4_logpacketstr __P((struct ip *, u_int32_t)); +extern const char *ipsec_logsastr __P((struct secasvar *)); -extern const char *ipsec4_logpacketstr __P((struct ip *, u_int32_t)); -extern const char *ipsec_logsastr __P((struct secasvar *)); +extern void ipsec_dumpmbuf __P((struct mbuf *)); -extern void ipsec_dumpmbuf __P((struct mbuf *)); - -extern int ipsec4_output __P((struct ipsec_output_state *, struct secpolicy *, +extern int ipsec4_output __P((struct ipsec_output_state *, struct secpolicy *, int)); - -extern int ipsec4_tunnel_validate __P((struct ip *, u_int, - struct secasvar *)); - -extern struct mbuf *ipsec_copypkt __P((struct mbuf *)); - +extern int ipsec4_tunnel_validate __P((struct ip *, u_int, struct secasvar *)); +extern struct mbuf *ipsec_copypkt __P((struct mbuf *)); +extern void ipsec_setsocket __P((struct mbuf *, struct socket *)); +extern struct socket *ipsec_getsocket __P((struct mbuf *)); #endif /*_KERNEL*/ #ifndef _KERNEL +extern caddr_t ipsec_set_policy __P((char *, int)); +extern int ipsec_get_policylen __P((caddr_t)); +extern char *ipsec_dump_policy __P((caddr_t, char *)); -extern caddr_t ipsec_set_policy __P((char *policy, int buflen)); -extern int ipsec_get_policylen __P((caddr_t buf)); -extern char *ipsec_dump_policy __P((caddr_t buf, char *delimiter)); - -extern char *ipsec_strerror __P((void)); +extern char *ipsec_strerror __P((void)); #endif /*!_KERNEL*/ #endif /*_NETINET6_IPSEC_H_*/ - Index: head/sys/netinet6/ipsec6.h =================================================================== --- head/sys/netinet6/ipsec6.h (revision 62586) +++ head/sys/netinet6/ipsec6.h (revision 62587) @@ -1,81 +1,80 @@ +/* $FreeBSD$ */ +/* $KAME: ipsec.h,v 1.33 2000/06/19 14:31:49 sakane Exp $ */ + /* - * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project. + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* - * IPsec controller part, only IPv6 related + * IPsec controller part. */ #ifndef _NETINET6_IPSEC6_H_ #define _NETINET6_IPSEC6_H_ +#include +#include + #ifdef _KERNEL +extern struct ipsecstat ipsec6stat; +extern struct secpolicy ip6_def_policy; +extern int ip6_esp_trans_deflev; +extern int ip6_esp_net_deflev; +extern int ip6_ah_trans_deflev; +extern int ip6_ah_net_deflev; +extern int ip6_ipsec_ecn; -#ifdef SYSCTL_DECL -SYSCTL_DECL(_net_inet6_ipsec6); -#endif - -extern struct ipsecstat ipsec6stat; -extern struct secpolicy ip6_def_policy; -extern int ip6_esp_trans_deflev; -extern int ip6_esp_net_deflev; -extern int ip6_ah_trans_deflev; -extern int ip6_ah_net_deflev; -extern int ip6_inbound_call_ike; -extern int ip6_ipsec_ecn; - -extern struct secpolicy *ipsec6_getpolicybysock +extern struct secpolicy *ipsec6_getpolicybysock __P((struct mbuf *, u_int, struct socket *, int *)); -extern struct secpolicy *ipsec6_getpolicybyaddr +extern struct secpolicy *ipsec6_getpolicybyaddr __P((struct mbuf *, u_int, int, int *)); -extern int ipsec6_in_reject_so __P((struct mbuf *, struct socket *)); -extern int ipsec6_delete_pcbpolicy __P((struct inpcb *)); -extern int ipsec6_set_policy __P((struct inpcb *inp, int optname, - caddr_t request, int priv)); -extern int ipsec6_get_policy - __P((struct inpcb *inp, caddr_t request, struct mbuf **mp)); -extern int ipsec6_in_reject __P((struct mbuf *, struct inpcb *)); +struct inpcb; -extern size_t ipsec6_hdrsiz __P((struct mbuf *, u_int, struct inpcb *)); +extern int ipsec6_in_reject_so __P((struct mbuf *, struct socket *)); +extern int ipsec6_delete_pcbpolicy __P((struct inpcb *)); +extern int ipsec6_set_policy __P((struct inpcb *inp, int optname, + caddr_t request, size_t len, int priv)); +extern int ipsec6_get_policy + __P((struct inpcb *inp, caddr_t request, size_t len, struct mbuf **mp)); +extern int ipsec6_in_reject __P((struct mbuf *, struct inpcb *)); -struct ip6_hdr; +extern size_t ipsec6_hdrsiz __P((struct mbuf *, u_int, struct inpcb *)); -extern const char *ipsec6_logpacketstr __P((struct ip6_hdr *, u_int32_t)); -extern int ipsec6_output_trans __P((struct ipsec_output_state *, u_char *, - struct mbuf *, struct secpolicy *, - int, int *)); -extern int ipsec6_output_tunnel __P((struct ipsec_output_state *, - struct secpolicy *, int)); -extern int ipsec6_tunnel_validate __P((struct ip6_hdr *, u_int, - struct secasvar *)); +struct ip6_hdr; +extern const char *ipsec6_logpacketstr __P((struct ip6_hdr *, u_int32_t)); +extern int ipsec6_output_trans __P((struct ipsec_output_state *, u_char *, + struct mbuf *, struct secpolicy *, int, int *)); +extern int ipsec6_output_tunnel __P((struct ipsec_output_state *, + struct secpolicy *, int)); +extern int ipsec6_tunnel_validate __P((struct ip6_hdr *, u_int, + struct secasvar *)); #endif /*_KERNEL*/ -#endif /* _NETINET6_IPSEC6_H_ */ + +#endif /*_NETINET6_IPSEC6_H_*/ Index: head/sys/netinet6/mld6.c =================================================================== --- head/sys/netinet6/mld6.c (revision 62586) +++ head/sys/netinet6/mld6.c (revision 62587) @@ -1,454 +1,470 @@ +/* $FreeBSD$ */ +/* $KAME: mld6.c,v 1.19 2000/05/05 11:01:03 sumikawa Exp $ */ + /* * Copyright (C) 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* * Copyright (c) 1988 Stephen Deering. * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Stephen Deering of Stanford University. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)igmp.c 8.1 (Berkeley) 7/19/93 */ -#include "opt_ipsec.h" +#include "opt_inet.h" +#include "opt_inet6.h" #include #include #include #include #include #include #include #include #include -#include -#include +#include #include -#include +#include #include #include /* * Protocol constants */ /* denotes that the MLD max response delay field specifies time in milliseconds */ -#define MLD6_TIMER_SCALE 1000 +#define MLD6_TIMER_SCALE 1000 /* * time between repetitions of a node's initial report of interest in a * multicast address(in seconds) */ -#define MLD6_UNSOLICITED_REPORT_INTERVAL 10 +#define MLD6_UNSOLICITED_REPORT_INTERVAL 10 -static struct ip6_pktopts ip6_opts; -static int mld6_timers_are_running; +static struct ip6_pktopts ip6_opts; +static int mld6_timers_are_running; /* XXX: These are necessary for KAME's link-local hack */ -static struct in6_addr mld6_all_nodes_linklocal = - IN6ADDR_LINKLOCAL_ALLNODES_INIT; -static struct in6_addr mld6_all_routers_linklocal = - IN6ADDR_LINKLOCAL_ALLROUTERS_INIT; +static struct in6_addr mld6_all_nodes_linklocal = IN6ADDR_LINKLOCAL_ALLNODES_INIT; +static struct in6_addr mld6_all_routers_linklocal = IN6ADDR_LINKLOCAL_ALLROUTERS_INIT; -static void mld6_sendpkt __P((struct in6_multi *, int, - const struct in6_addr *)); +static void mld6_sendpkt __P((struct in6_multi *, int, const struct in6_addr *)); void mld6_init() { static u_int8_t hbh_buf[8]; struct ip6_hbh *hbh = (struct ip6_hbh *)hbh_buf; u_int16_t rtalert_code = htons((u_int16_t)IP6OPT_RTALERT_MLD); mld6_timers_are_running = 0; /* ip6h_nxt will be fill in later */ - hbh->ip6h_len = 0; /* (8 >> 3) - 1*/ + hbh->ip6h_len = 0; /* (8 >> 3) - 1 */ /* XXX: grotty hard coding... */ hbh_buf[2] = IP6OPT_PADN; /* 2 byte padding */ hbh_buf[3] = 0; hbh_buf[4] = IP6OPT_RTALERT; hbh_buf[5] = IP6OPT_RTALERT_LEN - 2; bcopy((caddr_t)&rtalert_code, &hbh_buf[6], sizeof(u_int16_t)); ip6_opts.ip6po_hbh = hbh; /* We will specify the hoplimit by a multicast option. */ ip6_opts.ip6po_hlim = -1; } void mld6_start_listening(in6m) struct in6_multi *in6m; { int s = splnet(); /* - * (draft-ietf-ipngwg-mld, page 10) + * RFC2710 page 10: * The node never sends a Report or Done for the link-scope all-nodes * address. * MLD messages are never sent for multicast addresses whose scope is 0 * (reserved) or 1 (node-local). */ mld6_all_nodes_linklocal.s6_addr16[1] = htons(in6m->in6m_ifp->if_index); /* XXX */ if (IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &mld6_all_nodes_linklocal) || IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) < IPV6_ADDR_SCOPE_LINKLOCAL) { in6m->in6m_timer = 0; in6m->in6m_state = MLD6_OTHERLISTENER; } else { mld6_sendpkt(in6m, MLD6_LISTENER_REPORT, NULL); in6m->in6m_timer = MLD6_RANDOM_DELAY( MLD6_UNSOLICITED_REPORT_INTERVAL * PR_FASTHZ); in6m->in6m_state = MLD6_IREPORTEDLAST; mld6_timers_are_running = 1; } splx(s); } void mld6_stop_listening(in6m) struct in6_multi *in6m; { mld6_all_nodes_linklocal.s6_addr16[1] = htons(in6m->in6m_ifp->if_index); /* XXX */ mld6_all_routers_linklocal.s6_addr16[1] = htons(in6m->in6m_ifp->if_index); /* XXX: necessary when mrouting */ if (in6m->in6m_state == MLD6_IREPORTEDLAST && (!IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &mld6_all_nodes_linklocal)) && IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) > IPV6_ADDR_SCOPE_NODELOCAL) mld6_sendpkt(in6m, MLD6_LISTENER_DONE, &mld6_all_routers_linklocal); } void mld6_input(m, off) struct mbuf *m; int off; { struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); - struct mld6_hdr *mldh = (struct mld6_hdr *)(mtod(m, caddr_t) + off); + struct mld6_hdr *mldh; struct ifnet *ifp = m->m_pkthdr.rcvif; struct in6_multi *in6m; struct in6_ifaddr *ia; struct ifmultiaddr *ifma; int timer; /* timer value in the MLD query header */ /* source address validation */ if (!IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_src)) { log(LOG_ERR, "mld6_input: src %s is not link-local\n", ip6_sprintf(&ip6->ip6_src)); /* - * spec(draft-ietf-ipngwg-mld) does not explicitly + * spec (RFC2710) does not explicitly * specify to discard the packet from a non link-local * source address. But we believe it's expected to do so. */ + m_freem(m); return; } +#ifndef PULLDOWN_TEST + IP6_EXTHDR_CHECK(m, off, sizeof(*mldh),); + mldh = (struct mld6_hdr *)(mtod(m, caddr_t) + off); +#else + IP6_EXTHDR_GET(mldh, struct mld6_hdr *, m, off, sizeof(*mldh)); + if (mldh == NULL) { + icmp6stat.icp6s_tooshort++; + return; + } +#endif + /* * In the MLD6 specification, there are 3 states and a flag. * * In Non-Listener state, we simply don't have a membership record. * In Delaying Listener state, our timer is running (in6m->in6m_timer) * In Idle Listener state, our timer is not running (in6m->in6m_timer==0) * * The flag is in6m->in6m_state, it is set to MLD6_OTHERLISTENER if * we have heard a report from another member, or MLD6_IREPORTEDLAST * if we sent the last report. */ switch(mldh->mld6_type) { case MLD6_LISTENER_QUERY: if (ifp->if_flags & IFF_LOOPBACK) break; if (!IN6_IS_ADDR_UNSPECIFIED(&mldh->mld6_addr) && !IN6_IS_ADDR_MULTICAST(&mldh->mld6_addr)) break; /* print error or log stat? */ if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld6_addr)) mldh->mld6_addr.s6_addr16[1] = htons(ifp->if_index); /* XXX */ /* - * - Start the timers in all of our membership records - * that the query applies to for the interface on - * which the query arrived excl. those that belong - * to the "all-nodes" group (ff02::1). - * - Restart any timer that is already running but has - * A value longer than the requested timeout. - * - Use the value specified in the query message as - * the maximum timeout. - */ + * - Start the timers in all of our membership records + * that the query applies to for the interface on + * which the query arrived excl. those that belong + * to the "all-nodes" group (ff02::1). + * - Restart any timer that is already running but has + * A value longer than the requested timeout. + * - Use the value specified in the query message as + * the maximum timeout. + */ IFP_TO_IA6(ifp, ia); if (ia == NULL) break; /* - * XXX: System timer resolution is too low to handle Max - * Response Delay, so set 1 to the internal timer even if - * the calculated value equals to zero when Max Response - * Delay is positive. - */ + * XXX: System timer resolution is too low to handle Max + * Response Delay, so set 1 to the internal timer even if + * the calculated value equals to zero when Max Response + * Delay is positive. + */ timer = ntohs(mldh->mld6_maxdelay)*PR_FASTHZ/MLD6_TIMER_SCALE; if (timer == 0 && mldh->mld6_maxdelay) timer = 1; mld6_all_nodes_linklocal.s6_addr16[1] = htons(ifp->if_index); /* XXX */ LIST_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_INET6) continue; in6m = (struct in6_multi *)ifma->ifma_protospec; if (IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &mld6_all_nodes_linklocal) || IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) < IPV6_ADDR_SCOPE_LINKLOCAL) continue; if (IN6_IS_ADDR_UNSPECIFIED(&mldh->mld6_addr) || IN6_ARE_ADDR_EQUAL(&mldh->mld6_addr, &in6m->in6m_addr)) { if (timer == 0) { /* send a report immediately */ mld6_sendpkt(in6m, MLD6_LISTENER_REPORT, NULL); in6m->in6m_timer = 0; /* reset timer */ in6m->in6m_state = MLD6_IREPORTEDLAST; - } else if (in6m->in6m_timer == 0 || /*idle state*/ + } + else if (in6m->in6m_timer == 0 || /*idle state*/ in6m->in6m_timer > timer) { in6m->in6m_timer = MLD6_RANDOM_DELAY(timer); mld6_timers_are_running = 1; } } } if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld6_addr)) mldh->mld6_addr.s6_addr16[1] = 0; /* XXX */ break; case MLD6_LISTENER_REPORT: /* - * For fast leave to work, we have to know that we are the - * last person to send a report for this group. Reports - * can potentially get looped back if we are a multicast - * router, so discard reports sourced by me. - * Note that it is impossible to check IFF_LOOPBACK flag of - * ifp for this purpose, since ip6_mloopback pass the physical - * interface to looutput. - */ + * For fast leave to work, we have to know that we are the + * last person to send a report for this group. Reports + * can potentially get looped back if we are a multicast + * router, so discard reports sourced by me. + * Note that it is impossible to check IFF_LOOPBACK flag of + * ifp for this purpose, since ip6_mloopback pass the physical + * interface to looutput. + */ if (m->m_flags & M_LOOP) /* XXX: grotty flag, but efficient */ break; if (!IN6_IS_ADDR_MULTICAST(&mldh->mld6_addr)) break; if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld6_addr)) mldh->mld6_addr.s6_addr16[1] = htons(ifp->if_index); /* XXX */ /* - * If we belong to the group being reported, stop - * our timer for that group. - */ + * If we belong to the group being reported, stop + * our timer for that group. + */ IN6_LOOKUP_MULTI(mldh->mld6_addr, ifp, in6m); if (in6m) { in6m->in6m_timer = 0; /* transit to idle state */ in6m->in6m_state = MLD6_OTHERLISTENER; /* clear flag */ } if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld6_addr)) mldh->mld6_addr.s6_addr16[1] = 0; /* XXX */ break; default: /* this is impossible */ log(LOG_ERR, "mld6_input: illegal type(%d)", mldh->mld6_type); break; } + + m_freem(m); } void mld6_fasttimeo() { register struct in6_multi *in6m; struct in6_multistep step; int s; /* * Quick check to see if any work needs to be done, in order * to minimize the overhead of fasttimo processing. */ if (!mld6_timers_are_running) return; s = splnet(); mld6_timers_are_running = 0; IN6_FIRST_MULTI(step, in6m); while (in6m != NULL) { if (in6m->in6m_timer == 0) { /* do nothing */ } else if (--in6m->in6m_timer == 0) { mld6_sendpkt(in6m, MLD6_LISTENER_REPORT, NULL); in6m->in6m_state = MLD6_IREPORTEDLAST; } else { mld6_timers_are_running = 1; } IN6_NEXT_MULTI(step, in6m); } splx(s); } static void mld6_sendpkt(in6m, type, dst) struct in6_multi *in6m; int type; const struct in6_addr *dst; { struct mbuf *mh, *md; struct mld6_hdr *mldh; struct ip6_hdr *ip6; struct ip6_moptions im6o; struct in6_ifaddr *ia; struct ifnet *ifp = in6m->in6m_ifp; struct ifnet *outif = NULL; /* * At first, find a link local address on the outgoing interface * to use as the source address of the MLD packet. */ - if ((ia = in6ifa_ifpforlinklocal(ifp)) == NULL) + if ((ia = in6ifa_ifpforlinklocal(ifp, IN6_IFF_NOTREADY|IN6_IFF_ANYCAST)) + == NULL) return; /* * Allocate mbufs to store ip6 header and MLD header. * We allocate 2 mbufs and make chain in advance because * it is more convenient when inserting the hop-by-hop option later. */ MGETHDR(mh, M_DONTWAIT, MT_HEADER); if (mh == NULL) return; MGET(md, M_DONTWAIT, MT_DATA); if (md == NULL) { m_free(mh); return; } mh->m_next = md; + mh->m_pkthdr.len = sizeof(struct ip6_hdr) + sizeof(struct mld6_hdr); mh->m_len = sizeof(struct ip6_hdr); MH_ALIGN(mh, sizeof(struct ip6_hdr)); /* fill in the ip6 header */ ip6 = mtod(mh, struct ip6_hdr *); ip6->ip6_flow = 0; - ip6->ip6_vfc = IPV6_VERSION; + ip6->ip6_vfc &= ~IPV6_VERSION_MASK; + ip6->ip6_vfc |= IPV6_VERSION; /* ip6_plen will be set later */ ip6->ip6_nxt = IPPROTO_ICMPV6; /* ip6_hlim will be set by im6o.im6o_multicast_hlim */ ip6->ip6_src = ia->ia_addr.sin6_addr; ip6->ip6_dst = dst ? *dst : in6m->in6m_addr; /* fill in the MLD header */ md->m_len = sizeof(struct mld6_hdr); mldh = mtod(md, struct mld6_hdr *); mldh->mld6_type = type; mldh->mld6_code = 0; mldh->mld6_cksum = 0; /* XXX: we assume the function will not be called for query messages */ mldh->mld6_maxdelay = 0; mldh->mld6_reserved = 0; mldh->mld6_addr = in6m->in6m_addr; if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld6_addr)) mldh->mld6_addr.s6_addr16[1] = 0; /* XXX */ mldh->mld6_cksum = in6_cksum(mh, IPPROTO_ICMPV6, sizeof(struct ip6_hdr), sizeof(struct mld6_hdr)); /* construct multicast option */ bzero(&im6o, sizeof(im6o)); im6o.im6o_multicast_ifp = ifp; im6o.im6o_multicast_hlim = 1; /* * Request loopback of the report if we are acting as a multicast * router, so that the process-level routing daemon can hear it. */ - im6o.im6o_multicast_loop = 0; + im6o.im6o_multicast_loop = (ip6_mrouter != NULL); /* increment output statictics */ icmp6stat.icp6s_outhist[type]++; ip6_output(mh, &ip6_opts, NULL, 0, &im6o, &outif); if (outif) { icmp6_ifstat_inc(outif, ifs6_out_msg); switch(type) { case MLD6_LISTENER_QUERY: icmp6_ifstat_inc(outif, ifs6_out_mldquery); break; case MLD6_LISTENER_REPORT: icmp6_ifstat_inc(outif, ifs6_out_mldreport); break; case MLD6_LISTENER_DONE: icmp6_ifstat_inc(outif, ifs6_out_mlddone); break; } } } Index: head/sys/netinet6/mld6_var.h =================================================================== --- head/sys/netinet6/mld6_var.h (revision 62586) +++ head/sys/netinet6/mld6_var.h (revision 62587) @@ -1,52 +1,53 @@ +/* $FreeBSD$ */ +/* $KAME: mld6_var.h,v 1.4 2000/03/25 07:23:54 sumikawa Exp $ */ + /* * Copyright (C) 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ #ifndef _NETINET6_MLD6_VAR_H_ -#define _NETINET6_MLD6_VAR_H_ +#define _NETINET6_MLD6_VAR_H_ #ifdef _KERNEL -#define MLD6_RANDOM_DELAY(X) (random() % (X) + 1) +#define MLD6_RANDOM_DELAY(X) (random() % (X) + 1) /* * States for MLD stop-listening processing */ -#define MLD6_OTHERLISTENER 0 -#define MLD6_IREPORTEDLAST 1 +#define MLD6_OTHERLISTENER 0 +#define MLD6_IREPORTEDLAST 1 void mld6_init __P((void)); void mld6_input __P((struct mbuf *, int)); void mld6_start_listening __P((struct in6_multi *)); void mld6_stop_listening __P((struct in6_multi *)); void mld6_fasttimeo __P((void)); #endif /* _KERNEL */ #endif /* _NETINET6_MLD6_VAR_H_ */ Index: head/sys/netinet6/nd6.c =================================================================== --- head/sys/netinet6/nd6.c (revision 62586) +++ head/sys/netinet6/nd6.c (revision 62587) @@ -1,1529 +1,1987 @@ +/* $FreeBSD$ */ +/* $KAME: nd6.c,v 1.68 2000/07/02 14:48:02 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* * XXX * KAME 970409 note: * BSD/OS version heavily modifies this code, related to llinfo. * Since we don't have BSD/OS version of net/route.c in our hand, * I left the code mostly as it was in 970310. -- itojun */ +#include "opt_inet.h" +#include "opt_inet6.h" + #include #include #include #include #include #include #include #include +#include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include #include -#include +#include #include "loop.h" #include -#define ND6_SLOWTIMER_INTERVAL (60 * 60) /* 1 hour */ -#define ND6_RECALC_REACHTM_INTERVAL (60 * 120) /* 2 hours */ +#define ND6_SLOWTIMER_INTERVAL (60 * 60) /* 1 hour */ +#define ND6_RECALC_REACHTM_INTERVAL (60 * 120) /* 2 hours */ -#define SIN6(s) ((struct sockaddr_in6 *)s) -#define SDL(s) ((struct sockaddr_dl *)s) +#define SIN6(s) ((struct sockaddr_in6 *)s) +#define SDL(s) ((struct sockaddr_dl *)s) /* timer values */ int nd6_prune = 1; /* walk list every 1 seconds */ int nd6_delay = 5; /* delay first probe time 5 second */ int nd6_umaxtries = 3; /* maximum unicast query */ int nd6_mmaxtries = 3; /* maximum multicast query */ int nd6_useloopback = 1; /* use loopback interface for local traffic */ -int nd6_proxyall = 0; /* enable Proxy Neighbor Advertisement */ +/* preventing too many loops in ND option parsing */ +int nd6_maxndopt = 10; /* max # of ND options allowed */ + +int nd6_maxnudhint = 0; /* max # of subsequent upper layer hints */ + /* for debugging? */ -static int nd6_inuse, nd6_allocated; +static int nd6_inuse, nd6_allocated; -struct llinfo_nd6 llinfo_nd6 = {&llinfo_nd6, &llinfo_nd6}; -struct nd_ifinfo *nd_ifinfo = NULL; -struct nd_drhead nd_defrouter = { 0 }; -struct nd_prhead nd_prefix = { 0 }; +struct llinfo_nd6 llinfo_nd6 = {&llinfo_nd6, &llinfo_nd6}; +static size_t nd_ifinfo_indexlim = 8; +struct nd_ifinfo *nd_ifinfo = NULL; +struct nd_drhead nd_defrouter; +struct nd_prhead nd_prefix = { 0 }; -int nd6_recalc_reachtm_interval = ND6_RECALC_REACHTM_INTERVAL; -static struct sockaddr_in6 all1_sa; +int nd6_recalc_reachtm_interval = ND6_RECALC_REACHTM_INTERVAL; +static struct sockaddr_in6 all1_sa; -static void nd6_slowtimo __P((void *)); +static void nd6_slowtimo __P((void *)); void nd6_init() { static int nd6_init_done = 0; int i; if (nd6_init_done) { log(LOG_NOTICE, "nd6_init called more than once(ignored)\n"); return; } all1_sa.sin6_family = AF_INET6; all1_sa.sin6_len = sizeof(struct sockaddr_in6); for (i = 0; i < sizeof(all1_sa.sin6_addr); i++) all1_sa.sin6_addr.s6_addr[i] = 0xff; + /* initialization of the default router list */ + TAILQ_INIT(&nd_defrouter); + nd6_init_done = 1; /* start timer */ timeout(nd6_slowtimo, (caddr_t)0, ND6_SLOWTIMER_INTERVAL * hz); } void nd6_ifattach(ifp) struct ifnet *ifp; { - static size_t if_indexlim = 8; /* * We have some arrays that should be indexed by if_index. * since if_index will grow dynamically, they should grow too. */ - if (nd_ifinfo == NULL || if_index >= if_indexlim) { + if (nd_ifinfo == NULL || if_index >= nd_ifinfo_indexlim) { size_t n; caddr_t q; - while (if_index >= if_indexlim) - if_indexlim <<= 1; + while (if_index >= nd_ifinfo_indexlim) + nd_ifinfo_indexlim <<= 1; /* grow nd_ifinfo */ - n = if_indexlim * sizeof(struct nd_ifinfo); + n = nd_ifinfo_indexlim * sizeof(struct nd_ifinfo); q = (caddr_t)malloc(n, M_IP6NDP, M_WAITOK); bzero(q, n); if (nd_ifinfo) { bcopy((caddr_t)nd_ifinfo, q, n/2); free((caddr_t)nd_ifinfo, M_IP6NDP); } nd_ifinfo = (struct nd_ifinfo *)q; } #define ND nd_ifinfo[ifp->if_index] + + /* don't initialize if called twice */ + if (ND.linkmtu) + return; + ND.linkmtu = ifindex2ifnet[ifp->if_index]->if_mtu; ND.chlim = IPV6_DEFHLIM; ND.basereachable = REACHABLE_TIME; ND.reachable = ND_COMPUTE_RTIME(ND.basereachable); ND.retrans = RETRANS_TIMER; ND.receivedra = 0; + ND.flags = ND6_IFF_PERFORMNUD; nd6_setmtu(ifp); #undef ND } /* * Reset ND level link MTU. This function is called when the physical MTU * changes, which means we might have to adjust the ND level MTU. */ void nd6_setmtu(ifp) struct ifnet *ifp; { #define MIN(a,b) ((a) < (b) ? (a) : (b)) struct nd_ifinfo *ndi = &nd_ifinfo[ifp->if_index]; u_long oldmaxmtu = ndi->maxmtu; u_long oldlinkmtu = ndi->linkmtu; switch(ifp->if_type) { case IFT_ARCNET: /* XXX MTU handling needs more work */ ndi->maxmtu = MIN(60480, ifp->if_mtu); break; case IFT_ETHER: ndi->maxmtu = MIN(ETHERMTU, ifp->if_mtu); break; case IFT_FDDI: ndi->maxmtu = MIN(FDDIIPMTU, ifp->if_mtu); break; case IFT_ATM: ndi->maxmtu = MIN(ATMMTU, ifp->if_mtu); break; default: ndi->maxmtu = ifp->if_mtu; break; } if (oldmaxmtu != ndi->maxmtu) { /* * If the ND level MTU is not set yet, or if the maxmtu * is reset to a smaller value than the ND level MTU, * also reset the ND level MTU. */ if (ndi->linkmtu == 0 || ndi->maxmtu < ndi->linkmtu) { ndi->linkmtu = ndi->maxmtu; /* also adjust in6_maxmtu if necessary. */ if (oldlinkmtu == 0) { /* * XXX: the case analysis is grotty, but * it is not efficient to call in6_setmaxmtu() * here when we are during the initialization * procedure. */ if (in6_maxmtu < ndi->linkmtu) in6_maxmtu = ndi->linkmtu; } else in6_setmaxmtu(); } } #undef MIN } void nd6_option_init(opt, icmp6len, ndopts) void *opt; int icmp6len; union nd_opts *ndopts; { bzero(ndopts, sizeof(*ndopts)); ndopts->nd_opts_search = (struct nd_opt_hdr *)opt; ndopts->nd_opts_last = (struct nd_opt_hdr *)(((u_char *)opt) + icmp6len); if (icmp6len == 0) { ndopts->nd_opts_done = 1; ndopts->nd_opts_search = NULL; } } /* * Take one ND option. */ struct nd_opt_hdr * nd6_option(ndopts) union nd_opts *ndopts; { struct nd_opt_hdr *nd_opt; int olen; if (!ndopts) panic("ndopts == NULL in nd6_option\n"); if (!ndopts->nd_opts_last) panic("uninitialized ndopts in nd6_option\n"); if (!ndopts->nd_opts_search) return NULL; if (ndopts->nd_opts_done) return NULL; nd_opt = ndopts->nd_opts_search; olen = nd_opt->nd_opt_len << 3; if (olen == 0) { /* * Message validation requires that all included * options have a length that is greater than zero. */ bzero(ndopts, sizeof(*ndopts)); return NULL; } ndopts->nd_opts_search = (struct nd_opt_hdr *)((caddr_t)nd_opt + olen); if (!(ndopts->nd_opts_search < ndopts->nd_opts_last)) { ndopts->nd_opts_done = 1; ndopts->nd_opts_search = NULL; } return nd_opt; } /* * Parse multiple ND options. * This function is much easier to use, for ND routines that do not need * multiple options of the same type. */ int nd6_options(ndopts) union nd_opts *ndopts; { struct nd_opt_hdr *nd_opt; int i = 0; if (!ndopts) panic("ndopts == NULL in nd6_options\n"); if (!ndopts->nd_opts_last) panic("uninitialized ndopts in nd6_options\n"); if (!ndopts->nd_opts_search) return 0; while (1) { nd_opt = nd6_option(ndopts); if (!nd_opt && !ndopts->nd_opts_last) { /* * Message validation requires that all included * options have a length that is greater than zero. */ bzero(ndopts, sizeof(*ndopts)); return -1; } if (!nd_opt) goto skip1; switch (nd_opt->nd_opt_type) { case ND_OPT_SOURCE_LINKADDR: case ND_OPT_TARGET_LINKADDR: case ND_OPT_MTU: case ND_OPT_REDIRECTED_HEADER: if (ndopts->nd_opt_array[nd_opt->nd_opt_type]) { printf("duplicated ND6 option found " "(type=%d)\n", nd_opt->nd_opt_type); /* XXX bark? */ } else { ndopts->nd_opt_array[nd_opt->nd_opt_type] = nd_opt; } break; case ND_OPT_PREFIX_INFORMATION: if (ndopts->nd_opt_array[nd_opt->nd_opt_type] == 0) { ndopts->nd_opt_array[nd_opt->nd_opt_type] = nd_opt; } ndopts->nd_opts_pi_end = (struct nd_opt_prefix_info *)nd_opt; break; default: /* * Unknown options must be silently ignored, * to accomodate future extension to the protocol. */ - log(LOG_INFO, + log(LOG_DEBUG, "nd6_options: unsupported option %d - " "option ignored\n", nd_opt->nd_opt_type); } skip1: i++; - if (i > 10) { + if (i > nd6_maxndopt) { + icmp6stat.icp6s_nd_toomanyopt++; printf("too many loop in nd opt\n"); break; } if (ndopts->nd_opts_done) break; } return 0; } /* * ND6 timer routine to expire default route list and prefix list */ void nd6_timer(ignored_arg) void *ignored_arg; { int s; register struct llinfo_nd6 *ln; register struct nd_defrouter *dr; register struct nd_prefix *pr; s = splnet(); timeout(nd6_timer, (caddr_t)0, nd6_prune * hz); ln = llinfo_nd6.ln_next; /* XXX BSD/OS separates this code -- itojun */ while (ln && ln != &llinfo_nd6) { struct rtentry *rt; struct ifnet *ifp; struct sockaddr_in6 *dst; struct llinfo_nd6 *next = ln->ln_next; + /* XXX: used for the DELAY case only: */ + struct nd_ifinfo *ndi = NULL; if ((rt = ln->ln_rt) == NULL) { ln = next; continue; } if ((ifp = rt->rt_ifp) == NULL) { ln = next; continue; } + ndi = &nd_ifinfo[ifp->if_index]; dst = (struct sockaddr_in6 *)rt_key(rt); if (ln->ln_expire > time_second) { ln = next; continue; } /* sanity check */ if (!rt) panic("rt=0 in nd6_timer(ln=%p)\n", ln); + if (rt->rt_llinfo && (struct llinfo_nd6 *)rt->rt_llinfo != ln) + panic("rt_llinfo(%p) is not equal to ln(%p)\n", + rt->rt_llinfo, ln); if (!dst) panic("dst=0 in nd6_timer(ln=%p)\n", ln); switch (ln->ln_state) { case ND6_LLINFO_INCOMPLETE: if (ln->ln_asked < nd6_mmaxtries) { ln->ln_asked++; ln->ln_expire = time_second + nd_ifinfo[ifp->if_index].retrans / 1000; nd6_ns_output(ifp, NULL, &dst->sin6_addr, ln, 0); } else { struct mbuf *m = ln->ln_hold; if (m) { if (rt->rt_ifp) { /* * Fake rcvif to make ICMP error * more helpful in diagnosing * for the receiver. * XXX: should we consider * older rcvif? */ m->m_pkthdr.rcvif = rt->rt_ifp; } icmp6_error(m, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADDR, 0); ln->ln_hold = NULL; } nd6_free(rt); } break; case ND6_LLINFO_REACHABLE: - if (ln->ln_expire) { + if (ln->ln_expire) ln->ln_state = ND6_LLINFO_STALE; - } break; /* * ND6_LLINFO_STALE state requires nothing for timer * routine. */ case ND6_LLINFO_DELAY: - ln->ln_asked = 1; - ln->ln_state = ND6_LLINFO_PROBE; - ln->ln_expire = time_second + - nd_ifinfo[ifp->if_index].retrans / 1000; - nd6_ns_output(ifp, &dst->sin6_addr, &dst->sin6_addr, - ln, 0); + if (ndi && (ndi->flags & ND6_IFF_PERFORMNUD) != 0) { + /* We need NUD */ + ln->ln_asked = 1; + ln->ln_state = ND6_LLINFO_PROBE; + ln->ln_expire = time_second + + ndi->retrans / 1000; + nd6_ns_output(ifp, &dst->sin6_addr, + &dst->sin6_addr, + ln, 0); + } else + ln->ln_state = ND6_LLINFO_STALE; /* XXX */ break; - case ND6_LLINFO_PROBE: if (ln->ln_asked < nd6_umaxtries) { ln->ln_asked++; ln->ln_expire = time_second + nd_ifinfo[ifp->if_index].retrans / 1000; nd6_ns_output(ifp, &dst->sin6_addr, &dst->sin6_addr, ln, 0); } else { nd6_free(rt); } break; case ND6_LLINFO_WAITDELETE: nd6_free(rt); break; } ln = next; } /* expire */ - dr = LIST_FIRST(&nd_defrouter); + dr = TAILQ_FIRST(&nd_defrouter); while (dr) { if (dr->expire && dr->expire < time_second) { struct nd_defrouter *t; - t = LIST_NEXT(dr, dr_entry); + t = TAILQ_NEXT(dr, dr_entry); defrtrlist_del(dr); dr = t; - } else - dr = LIST_NEXT(dr, dr_entry); + } else { + dr = TAILQ_NEXT(dr, dr_entry); + } } - pr = LIST_FIRST(&nd_prefix); + pr = nd_prefix.lh_first; while (pr) { struct in6_ifaddr *ia6; struct in6_addrlifetime *lt6; if (IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr)) ia6 = NULL; else ia6 = in6ifa_ifpwithaddr(pr->ndpr_ifp, &pr->ndpr_addr); if (ia6) { /* check address lifetime */ lt6 = &ia6->ia6_lifetime; if (lt6->ia6t_preferred && lt6->ia6t_preferred < time_second) ia6->ia6_flags |= IN6_IFF_DEPRECATED; if (lt6->ia6t_expire && lt6->ia6t_expire < time_second) { if (!IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr)) in6_ifdel(pr->ndpr_ifp, &pr->ndpr_addr); /* xxx ND_OPT_PI_FLAG_ONLINK processing */ } } /* * check prefix lifetime. * since pltime is just for autoconf, pltime processing for * prefix is not necessary. * * we offset expire time by NDPR_KEEP_EXPIRE, so that we * can use the old prefix information to validate the * next prefix information to come. See prelist_update() * for actual validation. */ if (pr->ndpr_expire && pr->ndpr_expire + NDPR_KEEP_EXPIRED < time_second) { struct nd_prefix *t; - t = LIST_NEXT(pr, ndpr_entry); + t = pr->ndpr_next; /* * address expiration and prefix expiration are * separate. NEVER perform in6_ifdel here. */ prelist_remove(pr); pr = t; } else - pr = LIST_NEXT(pr, ndpr_entry); + pr = pr->ndpr_next; } splx(s); } +/* + * Nuke neighbor cache/prefix/default router management table, right before + * ifp goes away. + */ +void +nd6_purge(ifp) + struct ifnet *ifp; +{ + struct llinfo_nd6 *ln, *nln; + struct nd_defrouter *dr, *ndr, drany; + struct nd_prefix *pr, *npr; + + /* Nuke default router list entries toward ifp */ + if ((dr = TAILQ_FIRST(&nd_defrouter)) != NULL) { + /* + * The first entry of the list may be stored in + * the routing table, so we'll delete it later. + */ + for (dr = TAILQ_NEXT(dr, dr_entry); dr; dr = ndr) { + ndr = TAILQ_NEXT(dr, dr_entry); + if (dr->ifp == ifp) + defrtrlist_del(dr); + } + dr = TAILQ_FIRST(&nd_defrouter); + if (dr->ifp == ifp) + defrtrlist_del(dr); + } + + /* Nuke prefix list entries toward ifp */ + for (pr = nd_prefix.lh_first; pr; pr = npr) { + npr = pr->ndpr_next; + if (pr->ndpr_ifp == ifp) { + if (!IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr)) + in6_ifdel(pr->ndpr_ifp, &pr->ndpr_addr); + prelist_remove(pr); + } + } + + /* cancel default outgoing interface setting */ + if (nd6_defifindex == ifp->if_index) + nd6_setdefaultiface(0); + + /* refresh default router list */ + bzero(&drany, sizeof(drany)); + defrouter_delreq(&drany, 0); + defrouter_select(); + + /* + * Nuke neighbor cache entries for the ifp. + * Note that rt->rt_ifp may not be the same as ifp, + * due to KAME goto ours hack. See RTM_RESOLVE case in + * nd6_rtrequest(), and ip6_input(). + */ + ln = llinfo_nd6.ln_next; + while (ln && ln != &llinfo_nd6) { + struct rtentry *rt; + struct sockaddr_dl *sdl; + + nln = ln->ln_next; + rt = ln->ln_rt; + if (rt && rt->rt_gateway && + rt->rt_gateway->sa_family == AF_LINK) { + sdl = (struct sockaddr_dl *)rt->rt_gateway; + if (sdl->sdl_index == ifp->if_index) + nd6_free(rt); + } + ln = nln; + } + + /* + * Neighbor cache entry for interface route will be retained + * with ND6_LLINFO_WAITDELETE state, by nd6_free(). Nuke it. + */ + ln = llinfo_nd6.ln_next; + while (ln && ln != &llinfo_nd6) { + struct rtentry *rt; + struct sockaddr_dl *sdl; + + nln = ln->ln_next; + rt = ln->ln_rt; + if (rt && rt->rt_gateway && + rt->rt_gateway->sa_family == AF_LINK) { + sdl = (struct sockaddr_dl *)rt->rt_gateway; + if (sdl->sdl_index == ifp->if_index) { + rtrequest(RTM_DELETE, rt_key(rt), + (struct sockaddr *)0, rt_mask(rt), 0, + (struct rtentry **)0); + } + } + ln = nln; + } +} + struct rtentry * nd6_lookup(addr6, create, ifp) struct in6_addr *addr6; int create; struct ifnet *ifp; { struct rtentry *rt; struct sockaddr_in6 sin6; bzero(&sin6, sizeof(sin6)); sin6.sin6_len = sizeof(struct sockaddr_in6); sin6.sin6_family = AF_INET6; sin6.sin6_addr = *addr6; +#ifdef SCOPEDROUTING + sin6.sin6_scope_id = in6_addr2scopeid(ifp, addr6); +#endif rt = rtalloc1((struct sockaddr *)&sin6, create, 0UL); if (rt && (rt->rt_flags & RTF_LLINFO) == 0) { /* * This is the case for the default route. * If we want to create a neighbor cache for the address, we * should free the route for the destination and allocate an * interface route. */ if (create) { RTFREE(rt); rt = 0; } } if (!rt) { if (create && ifp) { + int e; + /* * If no route is available and create is set, * we allocate a host route for the destination * and treat it like an interface route. * This hack is necessary for a neighbor which can't * be covered by our own prefix. */ struct ifaddr *ifa = ifaof_ifpforaddr((struct sockaddr *)&sin6, ifp); if (ifa == NULL) return(NULL); /* * Create a new route. RTF_LLINFO is necessary * to create a Neighbor Cache entry for the * destination in nd6_rtrequest which will be * called in rtequest via ifa->ifa_rtrequest. */ - if (rtrequest(RTM_ADD, (struct sockaddr *)&sin6, - ifa->ifa_addr, - (struct sockaddr *)&all1_sa, - (ifa->ifa_flags | - RTF_HOST | RTF_LLINFO) & ~RTF_CLONING, - &rt)) + if ((e = rtrequest(RTM_ADD, (struct sockaddr *)&sin6, + ifa->ifa_addr, + (struct sockaddr *)&all1_sa, + (ifa->ifa_flags | + RTF_HOST | RTF_LLINFO) & + ~RTF_CLONING, + &rt)) != 0) log(LOG_ERR, "nd6_lookup: failed to add route for a " - "neighbor(%s)\n", ip6_sprintf(addr6)); + "neighbor(%s), errno=%d\n", + ip6_sprintf(addr6), e); if (rt == NULL) return(NULL); if (rt->rt_llinfo) { struct llinfo_nd6 *ln = (struct llinfo_nd6 *)rt->rt_llinfo; ln->ln_state = ND6_LLINFO_NOSTATE; } } else return(NULL); } rt->rt_refcnt--; /* * Validation for the entry. * XXX: we can't use rt->rt_ifp to check for the interface, since * it might be the loopback interface if the entry is for our * own address on a non-loopback interface. Instead, we should * use rt->rt_ifa->ifa_ifp, which would specify the REAL interface. */ if ((rt->rt_flags & RTF_GATEWAY) || (rt->rt_flags & RTF_LLINFO) == 0 || rt->rt_gateway->sa_family != AF_LINK || (ifp && rt->rt_ifa->ifa_ifp != ifp)) { if (create) { log(LOG_DEBUG, "nd6_lookup: failed to lookup %s (if = %s)\n", ip6_sprintf(addr6), ifp ? if_name(ifp) : "unspec"); /* xxx more logs... kazu */ } return(0); } return(rt); } /* * Detect if a given IPv6 address identifies a neighbor on a given link. * XXX: should take care of the destination of a p2p link? */ int nd6_is_addr_neighbor(addr, ifp) - struct in6_addr *addr; + struct sockaddr_in6 *addr; struct ifnet *ifp; { register struct ifaddr *ifa; int i; #define IFADDR6(a) ((((struct in6_ifaddr *)(a))->ia_addr).sin6_addr) #define IFMASK6(a) ((((struct in6_ifaddr *)(a))->ia_prefixmask).sin6_addr) - /* A link-local address is always a neighbor. */ - if (IN6_IS_ADDR_LINKLOCAL(addr)) + /* + * A link-local address is always a neighbor. + * XXX: we should use the sin6_scope_id field rather than the embedded + * interface index. + */ + if (IN6_IS_ADDR_LINKLOCAL(&addr->sin6_addr) && + ntohs(*(u_int16_t *)&addr->sin6_addr.s6_addr[2]) == ifp->if_index) return(1); /* * If the address matches one of our addresses, * it should be a neighbor. */ - TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) + for (ifa = ifp->if_addrlist.tqh_first; + ifa; + ifa = ifa->ifa_list.tqe_next) { if (ifa->ifa_addr->sa_family != AF_INET6) next: continue; for (i = 0; i < 4; i++) { - if ((IFADDR6(ifa).s6_addr32[i] ^ addr->s6_addr32[i]) & + if ((IFADDR6(ifa).s6_addr32[i] ^ + addr->sin6_addr.s6_addr32[i]) & IFMASK6(ifa).s6_addr32[i]) goto next; } return(1); } /* * Even if the address matches none of our addresses, it might be * in the neighbor cache. */ - if (nd6_lookup(addr, 0, ifp)) + if (nd6_lookup(&addr->sin6_addr, 0, ifp)) return(1); return(0); #undef IFADDR6 #undef IFMASK6 } /* * Free an nd6 llinfo entry. */ void nd6_free(rt) struct rtentry *rt; { struct llinfo_nd6 *ln = (struct llinfo_nd6 *)rt->rt_llinfo; struct sockaddr_dl *sdl; + struct in6_addr in6 = ((struct sockaddr_in6 *)rt_key(rt))->sin6_addr; + struct nd_defrouter *dr; - if (ln->ln_router) { - /* remove from default router list */ - struct nd_defrouter *dr; - struct in6_addr *in6; - int s; - in6 = &((struct sockaddr_in6 *)rt_key(rt))->sin6_addr; + /* + * Clear all destination cache entries for the neighbor. + * XXX: is it better to restrict this to hosts? + */ + pfctlinput(PRC_HOSTDEAD, rt_key(rt)); + if (!ip6_forwarding && ip6_accept_rtadv) { /* XXX: too restrictive? */ + int s; s = splnet(); - dr = defrouter_lookup(&((struct sockaddr_in6 *)rt_key(rt))-> - sin6_addr, + dr = defrouter_lookup(&((struct sockaddr_in6 *)rt_key(rt))->sin6_addr, rt->rt_ifp); - if (dr) - defrtrlist_del(dr); - else if (!ip6_forwarding && ip6_accept_rtadv) { + if (ln->ln_router || dr) { /* - * rt6_flush must be called in any case. - * see the comment in nd6_na_input(). + * rt6_flush must be called whether or not the neighbor + * is in the Default Router List. + * See a corresponding comment in nd6_na_input(). */ - rt6_flush(in6, rt->rt_ifp); + rt6_flush(&in6, rt->rt_ifp); } + + if (dr) { + /* + * Unreachablity of a router might affect the default + * router selection and on-link detection of advertised + * prefixes. + */ + + /* + * Temporarily fake the state to choose a new default + * router and to perform on-link determination of + * prefixes coreectly. + * Below the state will be set correctly, + * or the entry itself will be deleted. + */ + ln->ln_state = ND6_LLINFO_INCOMPLETE; + + if (dr == TAILQ_FIRST(&nd_defrouter)) { + /* + * It is used as the current default router, + * so we have to move it to the end of the + * list and choose a new one. + * XXX: it is not very efficient if this is + * the only router. + */ + TAILQ_REMOVE(&nd_defrouter, dr, dr_entry); + TAILQ_INSERT_TAIL(&nd_defrouter, dr, dr_entry); + + defrouter_select(); + } + pfxlist_onlink_check(); + } splx(s); } - + if (rt->rt_refcnt > 0 && (sdl = SDL(rt->rt_gateway)) && - sdl->sdl_family == AF_LINK) { + sdl->sdl_family == AF_LINK) { sdl->sdl_alen = 0; ln->ln_state = ND6_LLINFO_WAITDELETE; ln->ln_asked = 0; rt->rt_flags &= ~RTF_REJECT; return; } - rtrequest(RTM_DELETE, rt_key(rt), (struct sockaddr *)0, rt_mask(rt), - 0, (struct rtentry **)0); + + rtrequest(RTM_DELETE, rt_key(rt), (struct sockaddr *)0, + rt_mask(rt), 0, (struct rtentry **)0); } /* * Upper-layer reachability hint for Neighbor Unreachability Detection. * * XXX cost-effective metods? */ void -nd6_nud_hint(rt, dst6) +nd6_nud_hint(rt, dst6, force) struct rtentry *rt; struct in6_addr *dst6; + int force; { struct llinfo_nd6 *ln; /* * If the caller specified "rt", use that. Otherwise, resolve the * routing table by supplied "dst6". */ if (!rt) { if (!dst6) return; if (!(rt = nd6_lookup(dst6, 0, NULL))) return; } - if ((rt->rt_flags & RTF_GATEWAY) - || (rt->rt_flags & RTF_LLINFO) == 0 - || !rt->rt_llinfo - || !rt->rt_gateway - || rt->rt_gateway->sa_family != AF_LINK) { + if ((rt->rt_flags & RTF_GATEWAY) != 0 || + (rt->rt_flags & RTF_LLINFO) == 0 || + !rt->rt_llinfo || !rt->rt_gateway || + rt->rt_gateway->sa_family != AF_LINK) { /* This is not a host route. */ return; } ln = (struct llinfo_nd6 *)rt->rt_llinfo; if (ln->ln_state < ND6_LLINFO_REACHABLE) return; + /* + * if we get upper-layer reachability confirmation many times, + * it is possible we have false information. + */ + if (!force) { + ln->ln_byhint++; + if (ln->ln_byhint > nd6_maxnudhint) + return; + } + ln->ln_state = ND6_LLINFO_REACHABLE; if (ln->ln_expire) ln->ln_expire = time_second + nd_ifinfo[rt->rt_ifp->if_index].reachable; } +#ifdef OLDIP6OUTPUT +/* + * Resolve an IP6 address into an ethernet address. If success, + * desten is filled in. If there is no entry in ndptab, + * set one up and multicast a solicitation for the IP6 address. + * Hold onto this mbuf and resend it once the address + * is finally resolved. A return value of 1 indicates + * that desten has been filled in and the packet should be sent + * normally; a 0 return indicates that the packet has been + * taken over here, either now or for later transmission. + */ +int +nd6_resolve(ifp, rt, m, dst, desten) + struct ifnet *ifp; + struct rtentry *rt; + struct mbuf *m; + struct sockaddr *dst; + u_char *desten; +{ + struct llinfo_nd6 *ln = (struct llinfo_nd6 *)NULL; + struct sockaddr_dl *sdl; + + if (m->m_flags & M_MCAST) { + switch (ifp->if_type) { + case IFT_ETHER: + case IFT_FDDI: + ETHER_MAP_IPV6_MULTICAST(&SIN6(dst)->sin6_addr, + desten); + return(1); + break; + case IFT_ARCNET: + *desten = 0; + return(1); + break; + default: + return(0); + } + } + if (rt && (rt->rt_flags & RTF_LLINFO) != 0) + ln = (struct llinfo_nd6 *)rt->rt_llinfo; + else { + if ((rt = nd6_lookup(&(SIN6(dst)->sin6_addr), 1, ifp)) != NULL) + ln = (struct llinfo_nd6 *)rt->rt_llinfo; + } + if (!ln || !rt) { + log(LOG_DEBUG, "nd6_resolve: can't allocate llinfo for %s\n", + ip6_sprintf(&(SIN6(dst)->sin6_addr))); + m_freem(m); + return(0); + } + sdl = SDL(rt->rt_gateway); + /* + * Ckeck the address family and length is valid, the address + * is resolved; otherwise, try to resolve. + */ + if (ln->ln_state >= ND6_LLINFO_REACHABLE + && sdl->sdl_family == AF_LINK + && sdl->sdl_alen != 0) { + bcopy(LLADDR(sdl), desten, sdl->sdl_alen); + if (ln->ln_state == ND6_LLINFO_STALE) { + ln->ln_asked = 0; + ln->ln_state = ND6_LLINFO_DELAY; + ln->ln_expire = time_second + nd6_delay; + } + return(1); + } + /* + * There is an ndp entry, but no ethernet address + * response yet. Replace the held mbuf with this + * latest one. + * + * XXX Does the code conform to rate-limiting rule? + * (RFC 2461 7.2.2) + */ + if (ln->ln_state == ND6_LLINFO_WAITDELETE || + ln->ln_state == ND6_LLINFO_NOSTATE) + ln->ln_state = ND6_LLINFO_INCOMPLETE; + if (ln->ln_hold) + m_freem(ln->ln_hold); + ln->ln_hold = m; + if (ln->ln_expire) { + rt->rt_flags &= ~RTF_REJECT; + if (ln->ln_asked < nd6_mmaxtries && + ln->ln_expire < time_second) { + ln->ln_asked++; + ln->ln_expire = time_second + + nd_ifinfo[ifp->if_index].retrans / 1000; + nd6_ns_output(ifp, NULL, &(SIN6(dst)->sin6_addr), + ln, 0); + } + } + return(0); +} +#endif /* OLDIP6OUTPUT */ + void nd6_rtrequest(req, rt, sa) int req; struct rtentry *rt; struct sockaddr *sa; /* xxx unused */ { struct sockaddr *gate = rt->rt_gateway; struct llinfo_nd6 *ln = (struct llinfo_nd6 *)rt->rt_llinfo; static struct sockaddr_dl null_sdl = {sizeof(null_sdl), AF_LINK}; struct ifnet *ifp = rt->rt_ifp; struct ifaddr *ifa; if (rt->rt_flags & RTF_GATEWAY) return; switch (req) { case RTM_ADD: /* * There is no backward compatibility :) * * if ((rt->rt_flags & RTF_HOST) == 0 && * SIN(rt_mask(rt))->sin_addr.s_addr != 0xffffffff) * rt->rt_flags |= RTF_CLONING; */ - if (rt->rt_flags & RTF_CLONING || rt->rt_flags & RTF_LLINFO) { + if (rt->rt_flags & (RTF_CLONING | RTF_LLINFO)) { /* * Case 1: This route should come from * a route to interface. RTF_LLINFO flag is set * for a host route whose destination should be * treated as on-link. */ rt_setgate(rt, rt_key(rt), (struct sockaddr *)&null_sdl); gate = rt->rt_gateway; SDL(gate)->sdl_type = ifp->if_type; SDL(gate)->sdl_index = ifp->if_index; if (ln) ln->ln_expire = time_second; +#if 1 if (ln && ln->ln_expire == 0) { /* cludge for desktops */ +#if 0 + printf("nd6_request: time.tv_sec is zero; " + "treat it as 1\n"); +#endif ln->ln_expire = 1; } +#endif if (rt->rt_flags & RTF_CLONING) break; } - /* Announce a new entry if requested. */ + /* + * In IPv4 code, we try to annonuce new RTF_ANNOUNCE entry here. + * We don't do that here since llinfo is not ready yet. + * + * There are also couple of other things to be discussed: + * - unsolicited NA code needs improvement beforehand + * - RFC2461 says we MAY send multicast unsolicited NA + * (7.2.6 paragraph 4), however, it also says that we + * SHOULD provide a mechanism to prevent multicast NA storm. + * we don't have anything like it right now. + * note that the mechanism need a mutual agreement + * between proxies, which means that we need to implement + * a new protocol, or new kludge. + * - from RFC2461 6.2.4, host MUST NOT send unsolicited NA. + * we need to check ip6forwarding before sending it. + * (or should we allow proxy ND configuration only for + * routers? there's no mention about proxy ND from hosts) + */ +#if 0 + /* XXX it does not work */ if (rt->rt_flags & RTF_ANNOUNCE) nd6_na_output(ifp, - &SIN6(rt_key(rt))->sin6_addr, - &SIN6(rt_key(rt))->sin6_addr, - ip6_forwarding ? ND_NA_FLAG_ROUTER : 0, - 1); + &SIN6(rt_key(rt))->sin6_addr, + &SIN6(rt_key(rt))->sin6_addr, + ip6_forwarding ? ND_NA_FLAG_ROUTER : 0, + 1, NULL); +#endif /* FALLTHROUGH */ case RTM_RESOLVE: - if (gate->sa_family != AF_LINK || - gate->sa_len < sizeof(null_sdl)) { - log(LOG_DEBUG, "nd6_rtrequest: bad gateway value\n"); - break; + if ((ifp->if_flags & IFF_POINTOPOINT) == 0) { + /* + * Address resolution isn't necessary for a point to + * point link, so we can skip this test for a p2p link. + */ + if (gate->sa_family != AF_LINK || + gate->sa_len < sizeof(null_sdl)) { + log(LOG_DEBUG, + "nd6_rtrequest: bad gateway value\n"); + break; + } + SDL(gate)->sdl_type = ifp->if_type; + SDL(gate)->sdl_index = ifp->if_index; } - SDL(gate)->sdl_type = ifp->if_type; - SDL(gate)->sdl_index = ifp->if_index; - if (ln != 0) + if (ln != NULL) break; /* This happens on a route change */ /* * Case 2: This route may come from cloning, or a manual route * add with a LL address. */ R_Malloc(ln, struct llinfo_nd6 *, sizeof(*ln)); rt->rt_llinfo = (caddr_t)ln; if (!ln) { log(LOG_DEBUG, "nd6_rtrequest: malloc failed\n"); break; } nd6_inuse++; nd6_allocated++; Bzero(ln, sizeof(*ln)); ln->ln_rt = rt; /* this is required for "ndp" command. - shin */ if (req == RTM_ADD) { /* * gate should have some valid AF_LINK entry, * and ln->ln_expire should have some lifetime * which is specified by ndp command. */ ln->ln_state = ND6_LLINFO_REACHABLE; + ln->ln_byhint = 0; } else { /* * When req == RTM_RESOLVE, rt is created and * initialized in rtrequest(), so rt_expire is 0. */ - ln->ln_state = ND6_LLINFO_INCOMPLETE; + ln->ln_state = ND6_LLINFO_NOSTATE; ln->ln_expire = time_second; } rt->rt_flags |= RTF_LLINFO; ln->ln_next = llinfo_nd6.ln_next; llinfo_nd6.ln_next = ln; ln->ln_prev = &llinfo_nd6; ln->ln_next->ln_prev = ln; /* * check if rt_key(rt) is one of my address assigned * to the interface. */ ifa = (struct ifaddr *)in6ifa_ifpwithaddr(rt->rt_ifp, &SIN6(rt_key(rt))->sin6_addr); if (ifa) { caddr_t macp = nd6_ifptomac(ifp); ln->ln_expire = 0; ln->ln_state = ND6_LLINFO_REACHABLE; + ln->ln_byhint = 0; if (macp) { Bcopy(macp, LLADDR(SDL(gate)), ifp->if_addrlen); SDL(gate)->sdl_alen = ifp->if_addrlen; } if (nd6_useloopback) { rt->rt_ifp = &loif[0]; /*XXX*/ /* * Make sure rt_ifa be equal to the ifaddr * corresponding to the address. * We need this because when we refer * rt_ifa->ia6_flags in ip6_input, we assume * that the rt_ifa points to the address instead * of the loopback address. */ if (ifa != rt->rt_ifa) { - rt->rt_ifa->ifa_refcnt--; + IFAFREE(rt->rt_ifa); ifa->ifa_refcnt++; rt->rt_ifa = ifa; } } + } else if (rt->rt_flags & RTF_ANNOUNCE) { + ln->ln_expire = 0; + ln->ln_state = ND6_LLINFO_REACHABLE; + ln->ln_byhint = 0; + + /* join solicited node multicast for proxy ND */ + if (ifp->if_flags & IFF_MULTICAST) { + struct in6_addr llsol; + int error; + + llsol = SIN6(rt_key(rt))->sin6_addr; + llsol.s6_addr16[0] = htons(0xff02); + llsol.s6_addr16[1] = htons(ifp->if_index); + llsol.s6_addr32[1] = 0; + llsol.s6_addr32[2] = htonl(1); + llsol.s6_addr8[12] = 0xff; + + (void)in6_addmulti(&llsol, ifp, &error); + if (error) + printf( +"nd6_rtrequest: could not join solicited node multicast (errno=%d)\n", error); + } } break; case RTM_DELETE: if (!ln) break; + /* leave from solicited node multicast for proxy ND */ + if ((rt->rt_flags & RTF_ANNOUNCE) != 0 && + (ifp->if_flags & IFF_MULTICAST) != 0) { + struct in6_addr llsol; + struct in6_multi *in6m; + + llsol = SIN6(rt_key(rt))->sin6_addr; + llsol.s6_addr16[0] = htons(0xff02); + llsol.s6_addr16[1] = htons(ifp->if_index); + llsol.s6_addr32[1] = 0; + llsol.s6_addr32[2] = htonl(1); + llsol.s6_addr8[12] = 0xff; + + IN6_LOOKUP_MULTI(llsol, ifp, in6m); + if (in6m) + in6_delmulti(in6m); + } nd6_inuse--; ln->ln_next->ln_prev = ln->ln_prev; ln->ln_prev->ln_next = ln->ln_next; ln->ln_prev = NULL; rt->rt_llinfo = 0; rt->rt_flags &= ~RTF_LLINFO; if (ln->ln_hold) m_freem(ln->ln_hold); Free((caddr_t)ln); } } void nd6_p2p_rtrequest(req, rt, sa) int req; struct rtentry *rt; struct sockaddr *sa; /* xxx unused */ { struct sockaddr *gate = rt->rt_gateway; static struct sockaddr_dl null_sdl = {sizeof(null_sdl), AF_LINK}; struct ifnet *ifp = rt->rt_ifp; struct ifaddr *ifa; if (rt->rt_flags & RTF_GATEWAY) return; switch (req) { case RTM_ADD: /* * There is no backward compatibility :) * * if ((rt->rt_flags & RTF_HOST) == 0 && * SIN(rt_mask(rt))->sin_addr.s_addr != 0xffffffff) * rt->rt_flags |= RTF_CLONING; */ if (rt->rt_flags & RTF_CLONING) { /* * Case 1: This route should come from * a route to interface. */ rt_setgate(rt, rt_key(rt), (struct sockaddr *)&null_sdl); gate = rt->rt_gateway; SDL(gate)->sdl_type = ifp->if_type; SDL(gate)->sdl_index = ifp->if_index; break; } /* Announce a new entry if requested. */ if (rt->rt_flags & RTF_ANNOUNCE) nd6_na_output(ifp, &SIN6(rt_key(rt))->sin6_addr, &SIN6(rt_key(rt))->sin6_addr, ip6_forwarding ? ND_NA_FLAG_ROUTER : 0, - 1); + 1, NULL); /* FALLTHROUGH */ case RTM_RESOLVE: /* * check if rt_key(rt) is one of my address assigned * to the interface. */ ifa = (struct ifaddr *)in6ifa_ifpwithaddr(rt->rt_ifp, &SIN6(rt_key(rt))->sin6_addr); if (ifa) { if (nd6_useloopback) { rt->rt_ifp = &loif[0]; /*XXX*/ } } break; } } int nd6_ioctl(cmd, data, ifp) u_long cmd; caddr_t data; struct ifnet *ifp; { struct in6_drlist *drl = (struct in6_drlist *)data; struct in6_prlist *prl = (struct in6_prlist *)data; struct in6_ndireq *ndi = (struct in6_ndireq *)data; struct in6_nbrinfo *nbi = (struct in6_nbrinfo *)data; + struct in6_ndifreq *ndif = (struct in6_ndifreq *)data; struct nd_defrouter *dr, any; struct nd_prefix *pr; struct rtentry *rt; int i = 0, error = 0; int s; switch (cmd) { case SIOCGDRLST_IN6: bzero(drl, sizeof(*drl)); s = splnet(); - dr = LIST_FIRST(&nd_defrouter); + dr = TAILQ_FIRST(&nd_defrouter); while (dr && i < DRLSTSIZ) { drl->defrouter[i].rtaddr = dr->rtaddr; if (IN6_IS_ADDR_LINKLOCAL(&drl->defrouter[i].rtaddr)) { /* XXX: need to this hack for KAME stack */ drl->defrouter[i].rtaddr.s6_addr16[1] = 0; } else log(LOG_ERR, "default router list contains a " "non-linklocal address(%s)\n", ip6_sprintf(&drl->defrouter[i].rtaddr)); drl->defrouter[i].flags = dr->flags; drl->defrouter[i].rtlifetime = dr->rtlifetime; drl->defrouter[i].expire = dr->expire; drl->defrouter[i].if_index = dr->ifp->if_index; i++; - dr = LIST_NEXT(dr, dr_entry); + dr = TAILQ_NEXT(dr, dr_entry); } splx(s); break; case SIOCGPRLST_IN6: + /* + * XXX meaning of fields, especialy "raflags", is very + * differnet between RA prefix list and RR/static prefix list. + * how about separating ioctls into two? + */ bzero(prl, sizeof(*prl)); s = splnet(); - pr = LIST_FIRST(&nd_prefix); + pr = nd_prefix.lh_first; while (pr && i < PRLSTSIZ) { struct nd_pfxrouter *pfr; int j; prl->prefix[i].prefix = pr->ndpr_prefix.sin6_addr; prl->prefix[i].raflags = pr->ndpr_raf; prl->prefix[i].prefixlen = pr->ndpr_plen; prl->prefix[i].vltime = pr->ndpr_vltime; prl->prefix[i].pltime = pr->ndpr_pltime; prl->prefix[i].if_index = pr->ndpr_ifp->if_index; prl->prefix[i].expire = pr->ndpr_expire; - pfr = LIST_FIRST(&pr->ndpr_advrtrs); + pfr = pr->ndpr_advrtrs.lh_first; j = 0; while(pfr) { if (j < DRLSTSIZ) { #define RTRADDR prl->prefix[i].advrtr[j] RTRADDR = pfr->router->rtaddr; if (IN6_IS_ADDR_LINKLOCAL(&RTRADDR)) { /* XXX: hack for KAME */ RTRADDR.s6_addr16[1] = 0; } else log(LOG_ERR, "a router(%s) advertises " "a prefix with " "non-link local address\n", ip6_sprintf(&RTRADDR)); #undef RTRADDR } j++; - pfr = LIST_NEXT(pfr, pfr_entry); + pfr = pfr->pfr_next; } prl->prefix[i].advrtrs = j; + prl->prefix[i].origin = PR_ORIG_RA; i++; - pr = LIST_NEXT(pr, ndpr_entry); + pr = pr->ndpr_next; } - splx(s); { struct rr_prefix *rpp; for (rpp = LIST_FIRST(&rr_prefix); rpp; rpp = LIST_NEXT(rpp, rp_entry)) { if (i >= PRLSTSIZ) break; prl->prefix[i].prefix = rpp->rp_prefix.sin6_addr; prl->prefix[i].raflags = rpp->rp_raf; prl->prefix[i].prefixlen = rpp->rp_plen; prl->prefix[i].vltime = rpp->rp_vltime; prl->prefix[i].pltime = rpp->rp_pltime; prl->prefix[i].if_index = rpp->rp_ifp->if_index; prl->prefix[i].expire = rpp->rp_expire; prl->prefix[i].advrtrs = 0; + prl->prefix[i].origin = rpp->rp_origin; i++; } } + splx(s); break; case SIOCGIFINFO_IN6: + if (!nd_ifinfo || i >= nd_ifinfo_indexlim) { + error = EINVAL; + break; + } ndi->ndi = nd_ifinfo[ifp->if_index]; break; - case SIOCSNDFLUSH_IN6: + case SIOCSIFINFO_FLAGS: + /* XXX: almost all other fields of ndi->ndi is unused */ + if (!nd_ifinfo || i >= nd_ifinfo_indexlim) { + error = EINVAL; + break; + } + nd_ifinfo[ifp->if_index].flags = ndi->ndi.flags; + break; + case SIOCSNDFLUSH_IN6: /* XXX: the ioctl name is confusing... */ /* flush default router list */ /* * xxx sumikawa: should not delete route if default * route equals to the top of default router list */ bzero(&any, sizeof(any)); defrouter_delreq(&any, 0); + defrouter_select(); /* xxx sumikawa: flush prefix list */ break; case SIOCSPFXFLUSH_IN6: { /* flush all the prefix advertised by routers */ struct nd_prefix *pr, *next; s = splnet(); - for (pr = LIST_FIRST(&nd_prefix); pr; pr = next) { - next = LIST_NEXT(pr, ndpr_entry); + for (pr = nd_prefix.lh_first; pr; pr = next) { + next = pr->ndpr_next; if (!IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr)) in6_ifdel(pr->ndpr_ifp, &pr->ndpr_addr); prelist_remove(pr); } splx(s); break; } case SIOCSRTRFLUSH_IN6: { /* flush all the default routers */ struct nd_defrouter *dr, *next; s = splnet(); - if ((dr = LIST_FIRST(&nd_defrouter)) != NULL) { + if ((dr = TAILQ_FIRST(&nd_defrouter)) != NULL) { /* * The first entry of the list may be stored in * the routing table, so we'll delete it later. */ - for (dr = LIST_NEXT(dr, dr_entry); dr; dr = next) { - next = LIST_NEXT(dr, dr_entry); + for (dr = TAILQ_NEXT(dr, dr_entry); dr; dr = next) { + next = TAILQ_NEXT(dr, dr_entry); defrtrlist_del(dr); } - defrtrlist_del(LIST_FIRST(&nd_defrouter)); + defrtrlist_del(TAILQ_FIRST(&nd_defrouter)); } splx(s); break; } case SIOCGNBRINFO_IN6: { struct llinfo_nd6 *ln; struct in6_addr nb_addr = nbi->addr; /* make local for safety */ /* * XXX: KAME specific hack for scoped addresses * XXXX: for other scopes than link-local? */ if (IN6_IS_ADDR_LINKLOCAL(&nbi->addr) || IN6_IS_ADDR_MC_LINKLOCAL(&nbi->addr)) { u_int16_t *idp = (u_int16_t *)&nb_addr.s6_addr[2]; if (*idp == 0) *idp = htons(ifp->if_index); } s = splnet(); if ((rt = nd6_lookup(&nb_addr, 0, ifp)) == NULL) { error = EINVAL; + splx(s); break; } ln = (struct llinfo_nd6 *)rt->rt_llinfo; nbi->state = ln->ln_state; nbi->asked = ln->ln_asked; nbi->isrouter = ln->ln_router; nbi->expire = ln->ln_expire; splx(s); break; } + case SIOCGDEFIFACE_IN6: /* XXX: should be implemented as a sysctl? */ + ndif->ifindex = nd6_defifindex; + break; + case SIOCSDEFIFACE_IN6: /* XXX: should be implemented as a sysctl? */ + return(nd6_setdefaultiface(ndif->ifindex)); + break; } return(error); } /* * Create neighbor cache entry and cache link-layer address, * on reception of inbound ND6 packets. (RS/RA/NS/redirect) */ struct rtentry * nd6_cache_lladdr(ifp, from, lladdr, lladdrlen, type, code) struct ifnet *ifp; struct in6_addr *from; char *lladdr; int lladdrlen; int type; /* ICMP6 type */ int code; /* type dependent information */ { struct rtentry *rt = NULL; struct llinfo_nd6 *ln = NULL; int is_newentry; struct sockaddr_dl *sdl = NULL; int do_update; int olladdr; int llchange; int newstate = 0; if (!ifp) panic("ifp == NULL in nd6_cache_lladdr"); if (!from) panic("from == NULL in nd6_cache_lladdr"); /* nothing must be updated for unspecified address */ if (IN6_IS_ADDR_UNSPECIFIED(from)) return NULL; /* * Validation about ifp->if_addrlen and lladdrlen must be done in * the caller. * * XXX If the link does not have link-layer adderss, what should * we do? (ifp->if_addrlen == 0) * Spec says nothing in sections for RA, RS and NA. There's small * description on it in NS section (RFC 2461 7.2.3). */ rt = nd6_lookup(from, 0, ifp); if (!rt) { +#if 0 + /* nothing must be done if there's no lladdr */ + if (!lladdr || !lladdrlen) + return NULL; +#endif + rt = nd6_lookup(from, 1, ifp); is_newentry = 1; } else is_newentry = 0; if (!rt) return NULL; if ((rt->rt_flags & (RTF_GATEWAY | RTF_LLINFO)) != RTF_LLINFO) { fail: nd6_free(rt); return NULL; } ln = (struct llinfo_nd6 *)rt->rt_llinfo; if (!ln) goto fail; if (!rt->rt_gateway) goto fail; if (rt->rt_gateway->sa_family != AF_LINK) goto fail; sdl = SDL(rt->rt_gateway); olladdr = (sdl->sdl_alen) ? 1 : 0; if (olladdr && lladdr) { if (bcmp(lladdr, LLADDR(sdl), ifp->if_addrlen)) llchange = 1; else llchange = 0; } else llchange = 0; /* * newentry olladdr lladdr llchange (*=record) * 0 n n -- (1) * 0 y n -- (2) * 0 n y -- (3) * STALE * 0 y y n (4) * * 0 y y y (5) * STALE * 1 -- n -- (6) NOSTATE(= PASSIVE) * 1 -- y -- (7) * STALE */ if (lladdr) { /*(3-5) and (7)*/ /* * Record source link-layer address * XXX is it dependent to ifp->if_type? */ sdl->sdl_alen = ifp->if_addrlen; bcopy(lladdr, LLADDR(sdl), ifp->if_addrlen); } if (!is_newentry) { if ((!olladdr && lladdr) /*(3)*/ || (olladdr && lladdr && llchange)) { /*(5)*/ do_update = 1; newstate = ND6_LLINFO_STALE; } else /*(1-2,4)*/ do_update = 0; } else { do_update = 1; if (!lladdr) /*(6)*/ newstate = ND6_LLINFO_NOSTATE; else /*(7)*/ newstate = ND6_LLINFO_STALE; } if (do_update) { /* * Update the state of the neighbor cache. */ ln->ln_state = newstate; if (ln->ln_state == ND6_LLINFO_STALE) { rt->rt_flags &= ~RTF_REJECT; if (ln->ln_hold) { - nd6_output(ifp, ln->ln_hold, +#ifdef OLDIP6OUTPUT + (*ifp->if_output)(ifp, ln->ln_hold, + rt_key(rt), rt); +#else + /* + * we assume ifp is not a p2p here, so just + * set the 2nd argument as the 1st one. + */ + nd6_output(ifp, ifp, ln->ln_hold, (struct sockaddr_in6 *)rt_key(rt), rt); +#endif ln->ln_hold = 0; } } else if (ln->ln_state == ND6_LLINFO_INCOMPLETE) { /* probe right away */ ln->ln_expire = time_second; } } /* * ICMP6 type dependent behavior. * * NS: clear IsRouter if new entry * RS: clear IsRouter * RA: set IsRouter if there's lladdr * redir: clear IsRouter if new entry * * RA case, (1): * The spec says that we must set IsRouter in the following cases: * - If lladdr exist, set IsRouter. This means (1-5). * - If it is old entry (!newentry), set IsRouter. This means (7). * So, based on the spec, in (1-5) and (7) cases we must set IsRouter. * A quetion arises for (1) case. (1) case has no lladdr in the * neighbor cache, this is similar to (6). * This case is rare but we figured that we MUST NOT set IsRouter. * * newentry olladdr lladdr llchange NS RS RA redir * D R * 0 n n -- (1) c ? s * 0 y n -- (2) c s s * 0 n y -- (3) c s s * 0 y y n (4) c s s * 0 y y y (5) c s s * 1 -- n -- (6) c c c s * 1 -- y -- (7) c c s c s * * (c=clear s=set) */ switch (type & 0xff) { case ND_NEIGHBOR_SOLICIT: /* * New entry must have is_router flag cleared. */ if (is_newentry) /*(6-7)*/ ln->ln_router = 0; break; case ND_REDIRECT: /* * If the icmp is a redirect to a better router, always set the * is_router flag. Otherwise, if the entry is newly created, * clear the flag. [RFC 2461, sec 8.3] - * */ if (code == ND_REDIRECT_ROUTER) ln->ln_router = 1; else if (is_newentry) /*(6-7)*/ ln->ln_router = 0; break; case ND_ROUTER_SOLICIT: /* * is_router flag must always be cleared. */ ln->ln_router = 0; break; case ND_ROUTER_ADVERT: /* * Mark an entry with lladdr as a router. */ if ((!is_newentry && (olladdr || lladdr)) /*(2-5)*/ || (is_newentry && lladdr)) { /*(7)*/ ln->ln_router = 1; } break; } return rt; } static void nd6_slowtimo(ignored_arg) void *ignored_arg; { int s = splnet(); register int i; register struct nd_ifinfo *nd6if; timeout(nd6_slowtimo, (caddr_t)0, ND6_SLOWTIMER_INTERVAL * hz); for (i = 1; i < if_index + 1; i++) { + if (!nd_ifinfo || i >= nd_ifinfo_indexlim) + continue; nd6if = &nd_ifinfo[i]; if (nd6if->basereachable && /* already initialized */ (nd6if->recalctm -= ND6_SLOWTIMER_INTERVAL) <= 0) { /* * Since reachable time rarely changes by router * advertisements, we SHOULD insure that a new random * value gets recomputed at least once every few hours. * (RFC 2461, 6.3.4) */ nd6if->recalctm = nd6_recalc_reachtm_interval; nd6if->reachable = ND_COMPUTE_RTIME(nd6if->basereachable); } } splx(s); } #define senderr(e) { error = (e); goto bad;} int -nd6_output(ifp, m0, dst, rt0) +nd6_output(ifp, origifp, m0, dst, rt0) register struct ifnet *ifp; + struct ifnet *origifp; struct mbuf *m0; struct sockaddr_in6 *dst; struct rtentry *rt0; { register struct mbuf *m = m0; register struct rtentry *rt = rt0; + struct sockaddr_in6 *gw6 = NULL; struct llinfo_nd6 *ln = NULL; int error = 0; if (IN6_IS_ADDR_MULTICAST(&dst->sin6_addr)) goto sendpkt; /* * XXX: we currently do not make neighbor cache on any interface - * other than ARCnet, Ethernet and FDDI. + * other than ARCnet, Ethernet, FDDI and GIF. + * + * draft-ietf-ngtrans-mech-06.txt says: + * - unidirectional tunnels needs no ND */ switch (ifp->if_type) { case IFT_ARCNET: case IFT_ETHER: case IFT_FDDI: + case IFT_GIF: /* XXX need more cases? */ break; default: goto sendpkt; } /* * next hop determination. This routine is derived from ether_outpout. */ if (rt) { if ((rt->rt_flags & RTF_UP) == 0) { if ((rt0 = rt = rtalloc1((struct sockaddr *)dst, 1, 0UL)) != NULL) { rt->rt_refcnt--; - if (rt->rt_ifp != ifp) - return nd6_output(ifp, m0, dst, rt); /* XXX: loop care? */ + if (rt->rt_ifp != ifp) { + /* XXX: loop care? */ + return nd6_output(ifp, origifp, m0, + dst, rt); + } } else senderr(EHOSTUNREACH); } + if (rt->rt_flags & RTF_GATEWAY) { + gw6 = (struct sockaddr_in6 *)rt->rt_gateway; + + /* + * We skip link-layer address resolution and NUD + * if the gateway is not a neighbor from ND point + * of view, regardless the value of the value of + * nd_ifinfo.flags. + * The second condition is a bit tricky: we skip + * if the gateway is our own address, which is + * sometimes used to install a route to a p2p link. + */ + if (!nd6_is_addr_neighbor(gw6, ifp) || + in6ifa_ifpwithaddr(ifp, &gw6->sin6_addr)) { + if (rt->rt_flags & RTF_REJECT) + senderr(EHOSTDOWN); + + /* + * We allow this kind of tricky route only + * when the outgoing interface is p2p. + * XXX: we may need a more generic rule here. + */ + if ((ifp->if_flags & IFF_POINTOPOINT) == 0) + senderr(EHOSTUNREACH); + + goto sendpkt; + } + if (rt->rt_gwroute == 0) goto lookup; if (((rt = rt->rt_gwroute)->rt_flags & RTF_UP) == 0) { rtfree(rt); rt = rt0; lookup: rt->rt_gwroute = rtalloc1(rt->rt_gateway, 1, 0UL); if ((rt = rt->rt_gwroute) == 0) senderr(EHOSTUNREACH); } } if (rt->rt_flags & RTF_REJECT) senderr(rt == rt0 ? EHOSTDOWN : EHOSTUNREACH); } /* * Address resolution or Neighbor Unreachability Detection * for the next hop. * At this point, the destination of the packet must be a unicast * or an anycast address(i.e. not a multicast). */ /* Look up the neighbor cache for the nexthop */ if (rt && (rt->rt_flags & RTF_LLINFO) != 0) ln = (struct llinfo_nd6 *)rt->rt_llinfo; else { - if ((rt = nd6_lookup(&dst->sin6_addr, 1, ifp)) != NULL) + /* + * Since nd6_is_addr_neighbor() internally calls nd6_lookup(), + * the condition below is not very efficient. But we believe + * it is tolerable, because this should be a rare case. + */ + if (nd6_is_addr_neighbor(dst, ifp) && + (rt = nd6_lookup(&dst->sin6_addr, 1, ifp)) != NULL) ln = (struct llinfo_nd6 *)rt->rt_llinfo; } if (!ln || !rt) { - log(LOG_DEBUG, "nd6_output: can't allocate llinfo for %s " - "(ln=%p, rt=%p)\n", - ip6_sprintf(&dst->sin6_addr), ln, rt); - senderr(EIO); /* XXX: good error? */ + if ((ifp->if_flags & IFF_POINTOPOINT) == 0 && + !(nd_ifinfo[ifp->if_index].flags & ND6_IFF_PERFORMNUD)) { + log(LOG_DEBUG, + "nd6_output: can't allocate llinfo for %s " + "(ln=%p, rt=%p)\n", + ip6_sprintf(&dst->sin6_addr), ln, rt); + senderr(EIO); /* XXX: good error? */ + } + + goto sendpkt; /* send anyway */ } + /* We don't have to do link-layer address resolution on a p2p link. */ + if ((ifp->if_flags & IFF_POINTOPOINT) != 0 && + ln->ln_state < ND6_LLINFO_REACHABLE) + ln->ln_state = ND6_LLINFO_STALE; /* * The first time we send a packet to a neighbor whose entry is * STALE, we have to change the state to DELAY and a sets a timer to * expire in DELAY_FIRST_PROBE_TIME seconds to ensure do * neighbor unreachability detection on expiration. * (RFC 2461 7.3.3) */ if (ln->ln_state == ND6_LLINFO_STALE) { ln->ln_asked = 0; ln->ln_state = ND6_LLINFO_DELAY; ln->ln_expire = time_second + nd6_delay; } /* * If the neighbor cache entry has a state other than INCOMPLETE * (i.e. its link-layer address is already reloved), just * send the packet. */ if (ln->ln_state > ND6_LLINFO_INCOMPLETE) goto sendpkt; /* * There is a neighbor cache entry, but no ethernet address * response yet. Replace the held mbuf (if any) with this * latest one. * * XXX Does the code conform to rate-limiting rule? * (RFC 2461 7.2.2) */ if (ln->ln_state == ND6_LLINFO_WAITDELETE || ln->ln_state == ND6_LLINFO_NOSTATE) ln->ln_state = ND6_LLINFO_INCOMPLETE; if (ln->ln_hold) m_freem(ln->ln_hold); ln->ln_hold = m; if (ln->ln_expire) { rt->rt_flags &= ~RTF_REJECT; if (ln->ln_asked < nd6_mmaxtries && ln->ln_expire < time_second) { ln->ln_asked++; ln->ln_expire = time_second + nd_ifinfo[ifp->if_index].retrans / 1000; nd6_ns_output(ifp, NULL, &dst->sin6_addr, ln, 0); } } return(0); sendpkt: + +#ifdef FAKE_LOOPBACK_IF + if (ifp->if_flags & IFF_LOOPBACK) { + return((*ifp->if_output)(origifp, m, (struct sockaddr *)dst, + rt)); + } +#endif return((*ifp->if_output)(ifp, m, (struct sockaddr *)dst, rt)); - + bad: if (m) m_freem(m); return (error); } #undef senderr int nd6_storelladdr(ifp, rt, m, dst, desten) struct ifnet *ifp; struct rtentry *rt; struct mbuf *m; struct sockaddr *dst; u_char *desten; { struct sockaddr_dl *sdl; if (m->m_flags & M_MCAST) { switch (ifp->if_type) { case IFT_ETHER: case IFT_FDDI: ETHER_MAP_IPV6_MULTICAST(&SIN6(dst)->sin6_addr, desten); return(1); break; case IFT_ARCNET: *desten = 0; return(1); default: return(0); } } if (rt == NULL || rt->rt_gateway->sa_family != AF_LINK) { printf("nd6_storelladdr: something odd happens\n"); return(0); } sdl = SDL(rt->rt_gateway); - if (sdl->sdl_alen != 0) - bcopy(LLADDR(sdl), desten, sdl->sdl_alen); + if (sdl->sdl_alen == 0) { + /* this should be impossible, but we bark here for debugging */ + printf("nd6_storelladdr: sdl_alen == 0\n"); + return(0); + } + bcopy(LLADDR(sdl), desten, sdl->sdl_alen); return(1); } Index: head/sys/netinet6/nd6.h =================================================================== --- head/sys/netinet6/nd6.h (revision 62586) +++ head/sys/netinet6/nd6.h (revision 62587) @@ -1,298 +1,335 @@ +/* $FreeBSD$ */ +/* $KAME: nd6.h,v 1.23 2000/06/04 12:54:57 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ #ifndef _NETINET6_ND6_H_ -#define _NETINET6_ND6_H_ +#define _NETINET6_ND6_H_ +/* see net/route.h, or net/if_inarp.h */ +#ifndef RTF_ANNOUNCE +#define RTF_ANNOUNCE RTF_PROTO2 +#endif + #include struct llinfo_nd6 { struct llinfo_nd6 *ln_next; struct llinfo_nd6 *ln_prev; struct rtentry *ln_rt; struct mbuf *ln_hold; /* last packet until resolved/timeout */ long ln_asked; /* number of queries already sent for this addr */ u_long ln_expire; /* lifetime for NDP state transition */ short ln_state; /* reachability state */ short ln_router; /* 2^0: ND6 router bit */ + int ln_byhint; /* # of times we made it reachable by UL hint */ }; -#define ND6_LLINFO_NOSTATE -2 -#define ND6_LLINFO_WAITDELETE -1 -#define ND6_LLINFO_INCOMPLETE 0 -#define ND6_LLINFO_REACHABLE 1 -#define ND6_LLINFO_STALE 2 -#define ND6_LLINFO_DELAY 3 -#define ND6_LLINFO_PROBE 4 +#define ND6_LLINFO_NOSTATE -2 +#define ND6_LLINFO_WAITDELETE -1 +#define ND6_LLINFO_INCOMPLETE 0 +#define ND6_LLINFO_REACHABLE 1 +#define ND6_LLINFO_STALE 2 +#define ND6_LLINFO_DELAY 3 +#define ND6_LLINFO_PROBE 4 +#define ND6_IS_LLINFO_PROBREACH(n) ((n)->ln_state > ND6_LLINFO_INCOMPLETE) + struct nd_ifinfo { - u_int32_t linkmtu; /* LinkMTU */ - u_int32_t maxmtu; /* Upper bound of LinkMTU */ - u_int32_t basereachable; /* BaseReachableTime */ - u_int32_t reachable; /* Reachable Time */ - u_int32_t retrans; /* Retrans Timer */ - int recalctm; /* BaseReacable re-calculation timer */ - u_int8_t chlim; /* CurHopLimit */ - u_int8_t receivedra; + u_int32_t linkmtu; /* LinkMTU */ + u_int32_t maxmtu; /* Upper bound of LinkMTU */ + u_int32_t basereachable; /* BaseReachableTime */ + u_int32_t reachable; /* Reachable Time */ + u_int32_t retrans; /* Retrans Timer */ + u_int32_t flags; /* Flags */ + int recalctm; /* BaseReacable re-calculation timer */ + u_int8_t chlim; /* CurHopLimit */ + u_int8_t receivedra; }; +#define ND6_IFF_PERFORMNUD 0x1 + struct in6_nbrinfo { - char ifname[IFNAMSIZ]; /* if name, e.g. "en0" */ - struct in6_addr addr; /* IPv6 address of the neighbor */ + char ifname[IFNAMSIZ]; /* if name, e.g. "en0" */ + struct in6_addr addr; /* IPv6 address of the neighbor */ long asked; /* number of queries already sent for this addr */ int isrouter; /* if it acts as a router */ int state; /* reachability state */ int expire; /* lifetime for NDP state transition */ }; -#define DRLSTSIZ 10 -#define PRLSTSIZ 10 +#define DRLSTSIZ 10 +#define PRLSTSIZ 10 struct in6_drlist { - char ifname[IFNAMSIZ]; + char ifname[IFNAMSIZ]; struct { struct in6_addr rtaddr; u_char flags; u_short rtlifetime; u_long expire; - u_short if_index; + u_short if_index; } defrouter[DRLSTSIZ]; }; struct in6_prlist { - char ifname[IFNAMSIZ]; + char ifname[IFNAMSIZ]; struct { struct in6_addr prefix; - struct prf_ra raflags; + struct prf_ra raflags; u_char prefixlen; + u_char origin; u_long vltime; u_long pltime; u_long expire; - u_short if_index; - u_short advrtrs; /* number of advertisement routers */ + u_short if_index; + u_short advrtrs; /* number of advertisement routers */ struct in6_addr advrtr[DRLSTSIZ]; /* XXX: explicit limit */ } prefix[PRLSTSIZ]; }; struct in6_ndireq { - char ifname[IFNAMSIZ]; - struct nd_ifinfo ndi; + char ifname[IFNAMSIZ]; + struct nd_ifinfo ndi; }; +struct in6_ndifreq { + char ifname[IFNAMSIZ]; + u_long ifindex; +}; + + /* protocol constants */ -#define MAX_RTR_SOLICITATION_DELAY 1 /*1sec*/ -#define RTR_SOLICITATION_INTERVAL 4 /*4sec*/ -#define MAX_RTR_SOLICITATIONS 3 +#define MAX_RTR_SOLICITATION_DELAY 1 /*1sec*/ +#define RTR_SOLICITATION_INTERVAL 4 /*4sec*/ +#define MAX_RTR_SOLICITATIONS 3 -#define ND6_INFINITE_LIFETIME 0xffffffff +#define ND6_INFINITE_LIFETIME 0xffffffff #ifdef _KERNEL /* node constants */ -#define MAX_REACHABLE_TIME 3600000 /* msec */ -#define REACHABLE_TIME 30000 /* msec */ -#define RETRANS_TIMER 1000 /* msec */ -#define MIN_RANDOM_FACTOR 512 /* 1024 * 0.5 */ -#define MAX_RANDOM_FACTOR 1536 /* 1024 * 1.5 */ -#define ND_COMPUTE_RTIME(x) \ +#define MAX_REACHABLE_TIME 3600000 /* msec */ +#define REACHABLE_TIME 30000 /* msec */ +#define RETRANS_TIMER 1000 /* msec */ +#define MIN_RANDOM_FACTOR 512 /* 1024 * 0.5 */ +#define MAX_RANDOM_FACTOR 1536 /* 1024 * 1.5 */ +#define ND_COMPUTE_RTIME(x) \ (((MIN_RANDOM_FACTOR * (x >> 10)) + (random() & \ ((MAX_RANDOM_FACTOR - MIN_RANDOM_FACTOR) * (x >> 10)))) /1000) +TAILQ_HEAD(nd_drhead, nd_defrouter); struct nd_defrouter { - LIST_ENTRY(nd_defrouter) dr_entry; + TAILQ_ENTRY(nd_defrouter) dr_entry; struct in6_addr rtaddr; u_char flags; u_short rtlifetime; u_long expire; - struct ifnet *ifp; + u_long advint; /* Mobile IPv6 addition (milliseconds) */ + u_long advint_expire; /* Mobile IPv6 addition */ + int advints_lost; /* Mobile IPv6 addition */ + struct ifnet *ifp; }; struct nd_prefix { - struct ifnet *ndpr_ifp; + struct ifnet *ndpr_ifp; LIST_ENTRY(nd_prefix) ndpr_entry; - struct sockaddr_in6 ndpr_prefix; /* prefix */ - struct in6_addr ndpr_mask; /* netmask derived from the prefix */ - struct in6_addr ndpr_addr; /* address that is derived from the prefix */ - u_int32_t ndpr_vltime; /* advertised valid lifetime */ - u_int32_t ndpr_pltime; /* advertised preferred lifetime */ - time_t ndpr_expire; /* expiration time of the prefix */ - time_t ndpr_preferred; /* preferred time of the prefix */ - struct prf_ra ndpr_flags; + struct sockaddr_in6 ndpr_prefix; /* prefix */ + struct in6_addr ndpr_mask; /* netmask derived from the prefix */ + struct in6_addr ndpr_addr; /* address that is derived from the prefix */ + u_int32_t ndpr_vltime; /* advertised valid lifetime */ + u_int32_t ndpr_pltime; /* advertised preferred lifetime */ + time_t ndpr_expire; /* expiration time of the prefix */ + time_t ndpr_preferred; /* preferred time of the prefix */ + struct prf_ra ndpr_flags; /* list of routers that advertise the prefix: */ LIST_HEAD(pr_rtrhead, nd_pfxrouter) ndpr_advrtrs; u_char ndpr_plen; struct ndpr_stateflags { /* if this prefix can be regarded as on-link */ - u_char onlink : 1; + u_char onlink : 1; } ndpr_stateflags; }; -#define ndpr_raf ndpr_flags -#define ndpr_raf_onlink ndpr_flags.onlink -#define ndpr_raf_auto ndpr_flags.autonomous +#define ndpr_next ndpr_entry.le_next -#define ndpr_statef_onlink ndpr_stateflags.onlink -#define ndpr_statef_addmark ndpr_stateflags.addmark +#define ndpr_raf ndpr_flags +#define ndpr_raf_onlink ndpr_flags.onlink +#define ndpr_raf_auto ndpr_flags.autonomous +#define ndpr_statef_onlink ndpr_stateflags.onlink +#define ndpr_statef_addmark ndpr_stateflags.addmark + /* * We keep expired prefix for certain amount of time, for validation purposes. * 1800s = MaxRtrAdvInterval */ -#define NDPR_KEEP_EXPIRED (1800 * 2) +#define NDPR_KEEP_EXPIRED (1800 * 2) /* * Message format for use in obtaining information about prefixes * from inet6 sysctl function */ struct inet6_ndpr_msghdr { u_short inpm_msglen; /* to skip over non-understood messages */ u_char inpm_version; /* future binary compatability */ u_char inpm_type; /* message type */ - struct in6_addr inpm_prefix; + struct in6_addr inpm_prefix; u_long prm_vltim; u_long prm_pltime; u_long prm_expire; u_long prm_preferred; - struct in6_prflags prm_flags; + struct in6_prflags prm_flags; u_short prm_index; /* index for associated ifp */ u_char prm_plen; /* length of prefix in bits */ }; -#define prm_raf_onlink prm_flags.prf_ra.onlink -#define prm_raf_auto prm_flags.prf_ra.autonomous +#define prm_raf_onlink prm_flags.prf_ra.onlink +#define prm_raf_auto prm_flags.prf_ra.autonomous -#define prm_statef_onlink prm_flags.prf_state.onlink +#define prm_statef_onlink prm_flags.prf_state.onlink -#define prm_rrf_decrvalid prm_flags.prf_rr.decrvalid -#define prm_rrf_decrprefd prm_flags.prf_rr.decrprefd +#define prm_rrf_decrvalid prm_flags.prf_rr.decrvalid +#define prm_rrf_decrprefd prm_flags.prf_rr.decrprefd -#define ifpr2ndpr(ifpr) ((struct nd_prefix *)(ifpr)) -#define ndpr2ifpr(ndpr) ((struct ifprefix *)(ndpr)) +#define ifpr2ndpr(ifpr) ((struct nd_prefix *)(ifpr)) +#define ndpr2ifpr(ndpr) ((struct ifprefix *)(ndpr)) struct nd_pfxrouter { LIST_ENTRY(nd_pfxrouter) pfr_entry; - struct nd_defrouter *router; +#define pfr_next pfr_entry.le_next + struct nd_defrouter *router; }; -LIST_HEAD(nd_drhead, nd_defrouter); LIST_HEAD(nd_prhead, nd_prefix); /* nd6.c */ -extern int nd6_prune; -extern int nd6_delay; -extern int nd6_umaxtries; -extern int nd6_mmaxtries; -extern int nd6_useloopback; -extern int nd6_proxyall; -extern struct llinfo_nd6 llinfo_nd6; -extern struct nd_ifinfo *nd_ifinfo; -extern struct nd_drhead nd_defrouter; -extern struct nd_prhead nd_prefix; +extern int nd6_prune; +extern int nd6_delay; +extern int nd6_umaxtries; +extern int nd6_mmaxtries; +extern int nd6_useloopback; +extern int nd6_maxnudhint; +extern struct llinfo_nd6 llinfo_nd6; +extern struct nd_ifinfo *nd_ifinfo; +extern struct nd_drhead nd_defrouter; +extern struct nd_prhead nd_prefix; +/* nd6_rtr.c */ +extern int nd6_defifindex; + union nd_opts { - struct nd_opt_hdr *nd_opt_array[9]; + struct nd_opt_hdr *nd_opt_array[9]; /*max = home agent info*/ struct { - struct nd_opt_hdr *zero; - struct nd_opt_hdr *src_lladdr; - struct nd_opt_hdr *tgt_lladdr; - struct nd_opt_prefix_info *pi_beg;/* multiple opts, start */ - struct nd_opt_rd_hdr *rh; - struct nd_opt_mtu *mtu; - struct nd_opt_hdr *search; /* multiple opts */ - struct nd_opt_hdr *last; /* multiple opts */ - int done; - struct nd_opt_prefix_info *pi_end;/* multiple opts, end */ + struct nd_opt_hdr *zero; + struct nd_opt_hdr *src_lladdr; + struct nd_opt_hdr *tgt_lladdr; + struct nd_opt_prefix_info *pi_beg;/* multiple opts, start */ + struct nd_opt_rd_hdr *rh; + struct nd_opt_mtu *mtu; + struct nd_opt_hdr *six; + struct nd_opt_advint *adv; + struct nd_opt_hai *hai; + struct nd_opt_hdr *search; /* multiple opts */ + struct nd_opt_hdr *last; /* multiple opts */ + int done; + struct nd_opt_prefix_info *pi_end;/* multiple opts, end */ } nd_opt_each; }; -#define nd_opts_src_lladdr nd_opt_each.src_lladdr -#define nd_opts_tgt_lladdr nd_opt_each.tgt_lladdr -#define nd_opts_pi nd_opt_each.pi_beg -#define nd_opts_pi_end nd_opt_each.pi_end -#define nd_opts_rh nd_opt_each.rh -#define nd_opts_mtu nd_opt_each.mtu -#define nd_opts_search nd_opt_each.search -#define nd_opts_last nd_opt_each.last -#define nd_opts_done nd_opt_each.done +#define nd_opts_src_lladdr nd_opt_each.src_lladdr +#define nd_opts_tgt_lladdr nd_opt_each.tgt_lladdr +#define nd_opts_pi nd_opt_each.pi_beg +#define nd_opts_pi_end nd_opt_each.pi_end +#define nd_opts_rh nd_opt_each.rh +#define nd_opts_mtu nd_opt_each.mtu +#define nd_opts_adv nd_opt_each.adv +#define nd_opts_hai nd_opt_each.hai +#define nd_opts_search nd_opt_each.search +#define nd_opts_last nd_opt_each.last +#define nd_opts_done nd_opt_each.done /* XXX: need nd6_var.h?? */ /* nd6.c */ -void nd6_init __P((void)); -void nd6_ifattach __P((struct ifnet *)); -int nd6_is_addr_neighbor __P((struct in6_addr *, struct ifnet *)); -void nd6_option_init __P((void *, int, union nd_opts *)); -struct nd_opt_hdr *nd6_option __P((union nd_opts *)); -int nd6_options __P((union nd_opts *)); +void nd6_init __P((void)); +void nd6_ifattach __P((struct ifnet *)); +int nd6_is_addr_neighbor __P((struct sockaddr_in6 *, struct ifnet *)); +void nd6_option_init __P((void *, int, union nd_opts *)); +struct nd_opt_hdr *nd6_option __P((union nd_opts *)); +int nd6_options __P((union nd_opts *)); struct rtentry *nd6_lookup __P((struct in6_addr *, int, struct ifnet *)); -void nd6_setmtu __P((struct ifnet *)); -void nd6_timer __P((void *)); -void nd6_free __P((struct rtentry *)); -void nd6_nud_hint __P((struct rtentry *, struct in6_addr *)); -int nd6_resolve __P((struct ifnet *, struct rtentry *, - struct mbuf *, struct sockaddr *, u_char *)); -void nd6_rtrequest __P((int, struct rtentry *, struct sockaddr *)); -void nd6_p2p_rtrequest __P((int, struct rtentry *, struct sockaddr *)); -int nd6_ioctl __P((u_long, caddr_t, struct ifnet *)); -struct rtentry *nd6_cache_lladdr __P((struct ifnet *, struct in6_addr *, - char *, int, int, int)); +void nd6_setmtu __P((struct ifnet *)); +void nd6_timer __P((void *)); +void nd6_purge __P((struct ifnet *)); +void nd6_free __P((struct rtentry *)); +void nd6_nud_hint __P((struct rtentry *, struct in6_addr *, int)); +int nd6_resolve __P((struct ifnet *, struct rtentry *, + struct mbuf *, struct sockaddr *, u_char *)); +void nd6_rtrequest __P((int, struct rtentry *, struct sockaddr *)); +void nd6_p2p_rtrequest __P((int, struct rtentry *, struct sockaddr *)); +int nd6_ioctl __P((u_long, caddr_t, struct ifnet *)); +struct rtentry *nd6_cache_lladdr __P((struct ifnet *, struct in6_addr *, + char *, int, int, int)); /* for test */ -int nd6_output __P((struct ifnet *, struct mbuf *, struct sockaddr_in6 *, - struct rtentry *)); -int nd6_storelladdr __P((struct ifnet *, struct rtentry *, struct mbuf *, - struct sockaddr *, u_char *)); +int nd6_output __P((struct ifnet *, struct ifnet *, struct mbuf *, + struct sockaddr_in6 *, struct rtentry *)); +int nd6_storelladdr __P((struct ifnet *, struct rtentry *, struct mbuf *, + struct sockaddr *, u_char *)); /* nd6_nbr.c */ -void nd6_na_input __P((struct mbuf *, int, int)); -void nd6_na_output __P((struct ifnet *, struct in6_addr *, - struct in6_addr *, u_long, int)); -void nd6_ns_input __P((struct mbuf *, int, int)); -void nd6_ns_output __P((struct ifnet *, struct in6_addr *, - struct in6_addr *, struct llinfo_nd6 *, int)); -caddr_t nd6_ifptomac __P((struct ifnet *)); -void nd6_dad_start __P((struct ifaddr *, int *)); -void nd6_dad_duplicated __P((struct ifaddr *)); +void nd6_na_input __P((struct mbuf *, int, int)); +void nd6_na_output __P((struct ifnet *, struct in6_addr *, + struct in6_addr *, u_long, int, struct sockaddr *)); +void nd6_ns_input __P((struct mbuf *, int, int)); +void nd6_ns_output __P((struct ifnet *, struct in6_addr *, + struct in6_addr *, struct llinfo_nd6 *, int)); +caddr_t nd6_ifptomac __P((struct ifnet *)); +void nd6_dad_start __P((struct ifaddr *, int *)); +void nd6_dad_duplicated __P((struct ifaddr *)); /* nd6_rtr.c */ -void nd6_rs_input __P((struct mbuf *, int, int)); -void nd6_ra_input __P((struct mbuf *, int, int)); -void prelist_del __P((struct nd_prefix *)); -void defrouter_addreq __P((struct nd_defrouter *)); -void defrouter_delreq __P((struct nd_defrouter *, int)); -void defrtrlist_del __P((struct nd_defrouter *)); -void prelist_remove __P((struct nd_prefix *)); -int prelist_update __P((struct nd_prefix *, struct nd_defrouter *, - struct mbuf *)); -struct nd_defrouter *defrouter_lookup __P((struct in6_addr *, - struct ifnet *)); -int in6_ifdel __P((struct ifnet *, struct in6_addr *)); -int in6_init_prefix_ltimes __P((struct nd_prefix *ndpr)); -void rt6_flush __P((struct in6_addr *, struct ifnet *)); +void nd6_rs_input __P((struct mbuf *, int, int)); +void nd6_ra_input __P((struct mbuf *, int, int)); +void prelist_del __P((struct nd_prefix *)); +void defrouter_addreq __P((struct nd_defrouter *)); +void defrouter_delreq __P((struct nd_defrouter *, int)); +void defrouter_select __P((void)); +void defrtrlist_del __P((struct nd_defrouter *)); +void prelist_remove __P((struct nd_prefix *)); +int prelist_update __P((struct nd_prefix *, struct nd_defrouter *, + struct mbuf *)); +void pfxlist_onlink_check __P((void)); +struct nd_defrouter *defrouter_lookup __P((struct in6_addr *, + struct ifnet *)); +int in6_ifdel __P((struct ifnet *, struct in6_addr *)); +int in6_init_prefix_ltimes __P((struct nd_prefix *ndpr)); +void rt6_flush __P((struct in6_addr *, struct ifnet *)); +int nd6_setdefaultiface __P((int)); #endif /* _KERNEL */ #endif /* _NETINET6_ND6_H_ */ Index: head/sys/netinet6/nd6_nbr.c =================================================================== --- head/sys/netinet6/nd6_nbr.c (revision 62586) +++ head/sys/netinet6/nd6_nbr.c (revision 62587) @@ -1,1121 +1,1331 @@ +/* $FreeBSD$ */ +/* $KAME: nd6_nbr.c,v 1.37 2000/06/04 12:46:13 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ +#include "opt_inet.h" +#include "opt_inet6.h" #include "opt_ipsec.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include -#include +#include +#ifdef IPSEC +#include +#ifdef INET6 +#include +#endif +#endif + #include -#define SDL(s) ((struct sockaddr_dl *)s) +#define SDL(s) ((struct sockaddr_dl *)s) -struct dadq; -static struct dadq *nd6_dad_find __P((struct ifaddr *)); -static void nd6_dad_timer __P((struct ifaddr *)); -static void nd6_dad_ns_input __P((struct ifaddr *)); -static void nd6_dad_na_input __P((struct ifaddr *)); +struct dadq; +static struct dadq *nd6_dad_find __P((struct ifaddr *)); +static void nd6_dad_timer __P((struct ifaddr *)); +static void nd6_dad_ns_output __P((struct dadq *, struct ifaddr *)); +static void nd6_dad_ns_input __P((struct ifaddr *)); +static void nd6_dad_na_input __P((struct ifaddr *)); -/* ignore NS in DAD - specwise incorrect, */ -int dad_ignore_ns = 0; +static int dad_ignore_ns = 0; /* ignore NS in DAD - specwise incorrect*/ +static int dad_maxtry = 15; /* max # of *tries* to transmit DAD packet */ /* * Input an Neighbor Solicitation Message. * * Based on RFC 2461 * Based on RFC 2462 (duplicated address detection) - * - * XXX proxy advertisement */ void nd6_ns_input(m, off, icmp6len) struct mbuf *m; int off, icmp6len; { struct ifnet *ifp = m->m_pkthdr.rcvif; struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); - struct nd_neighbor_solicit *nd_ns - = (struct nd_neighbor_solicit *)((caddr_t)ip6 + off); + struct nd_neighbor_solicit *nd_ns; struct in6_addr saddr6 = ip6->ip6_src; struct in6_addr daddr6 = ip6->ip6_dst; - struct in6_addr taddr6 = nd_ns->nd_ns_target; + struct in6_addr taddr6; struct in6_addr myaddr6; char *lladdr = NULL; struct ifaddr *ifa; int lladdrlen = 0; int anycast = 0, proxy = 0, tentative = 0; int tlladdr; union nd_opts ndopts; + struct sockaddr_dl *proxydl = NULL; if (ip6->ip6_hlim != 255) { log(LOG_ERR, "nd6_ns_input: invalid hlim %d\n", ip6->ip6_hlim); - return; + goto freeit; } if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) { /* dst has to be solicited node multicast address. */ if (daddr6.s6_addr16[0] == IPV6_ADDR_INT16_MLL /*don't check ifindex portion*/ && daddr6.s6_addr32[1] == 0 && daddr6.s6_addr32[2] == IPV6_ADDR_INT32_ONE && daddr6.s6_addr8[12] == 0xff) { ; /*good*/ } else { log(LOG_INFO, "nd6_ns_input: bad DAD packet " "(wrong ip6 dst)\n"); goto bad; } } +#ifndef PULLDOWN_TEST + IP6_EXTHDR_CHECK(m, off, icmp6len,); + nd_ns = (struct nd_neighbor_solicit *)((caddr_t)ip6 + off); +#else + IP6_EXTHDR_GET(nd_ns, struct nd_neighbor_solicit *, m, off, icmp6len); + if (nd_ns == NULL) { + icmp6stat.icp6s_tooshort++; + return; + } +#endif + taddr6 = nd_ns->nd_ns_target; + if (IN6_IS_ADDR_MULTICAST(&taddr6)) { log(LOG_INFO, "nd6_ns_input: bad NS target (multicast)\n"); goto bad; } if (IN6_IS_SCOPE_LINKLOCAL(&taddr6)) taddr6.s6_addr16[1] = htons(ifp->if_index); icmp6len -= sizeof(*nd_ns); nd6_option_init(nd_ns + 1, icmp6len, &ndopts); if (nd6_options(&ndopts) < 0) { log(LOG_INFO, "nd6_ns_input: invalid ND option, ignored\n"); goto bad; } if (ndopts.nd_opts_src_lladdr) { lladdr = (char *)(ndopts.nd_opts_src_lladdr +1); lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3; } if (IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src) && lladdr) { log(LOG_INFO, "nd6_ns_input: bad DAD packet " "(link-layer address option)\n"); goto bad; } /* * Attaching target link-layer address to the NA? * (RFC 2461 7.2.4) * * NS IP dst is unicast/anycast MUST NOT add * NS IP dst is solicited-node multicast MUST add * * In implementation, we add target link-layer address by default. * We do not add one in MUST NOT cases. */ +#if 0 /* too much! */ + ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &daddr6); + if (ifa && (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST)) + tlladdr = 0; + else +#endif if (!IN6_IS_ADDR_MULTICAST(&daddr6)) tlladdr = 0; else tlladdr = 1; /* * Target address (taddr6) must be either: * (1) Valid unicast/anycast address for my receiving interface, * (2) Unicast address for which I'm offering proxy service, or * (3) "tentative" address on which DAD is being performed. */ /* (1) and (3) check. */ ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &taddr6); /* (2) check. */ - if (!ifa && nd6_proxyall) { + if (!ifa) { struct rtentry *rt; struct sockaddr_in6 tsin6; bzero(&tsin6, sizeof tsin6); tsin6.sin6_len = sizeof(struct sockaddr_in6); tsin6.sin6_family = AF_INET6; tsin6.sin6_addr = taddr6; rt = rtalloc1((struct sockaddr *)&tsin6, 0, 0); - if (rt && rt->rt_ifp != ifp) { + if (rt && (rt->rt_flags & RTF_ANNOUNCE) != 0 && + rt->rt_gateway->sa_family == AF_LINK) { /* - * search link local addr for ifp, and use it for - * proxy NA. + * proxy NDP for single entry */ - ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp); - if (ifa) + ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp, + IN6_IFF_NOTREADY|IN6_IFF_ANYCAST); + if (ifa) { proxy = 1; + proxydl = SDL(rt->rt_gateway); + } } - rtfree(rt); + if (rt) + rtfree(rt); } if (!ifa) { /* * We've got a NS packet, and we don't have that adddress * assigned for us. We MUST silently ignore it. * See RFC2461 7.2.3. */ - return; + goto freeit; } myaddr6 = *IFA_IN6(ifa); anycast = ((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST; tentative = ((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_TENTATIVE; if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DUPLICATED) - return; + goto freeit; if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { log(LOG_INFO, "nd6_ns_input: lladdrlen mismatch for %s " "(if %d, NS packet %d)\n", ip6_sprintf(&taddr6), ifp->if_addrlen, lladdrlen - 2); } if (IN6_ARE_ADDR_EQUAL(&myaddr6, &saddr6)) { log(LOG_INFO, "nd6_ns_input: duplicate IP6 address %s\n", ip6_sprintf(&saddr6)); - return; + goto freeit; } /* * We have neighbor solicitation packet, with target address equals to * one of my tentative address. * * src addr how to process? * --- --- * multicast of course, invalid (rejected in ip6_input) * unicast somebody is doing address resolution -> ignore * unspec dup address detection * * The processing is defined in RFC 2462. */ if (tentative) { /* * If source address is unspecified address, it is for * duplicated address detection. * * If not, the packet is for addess resolution; * silently ignore it. */ if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) nd6_dad_ns_input(ifa); - return; + goto freeit; } /* * If the source address is unspecified address, entries must not * be created or updated. * It looks that sender is performing DAD. Output NA toward * all-node multicast address, to tell the sender that I'm using * the address. * S bit ("solicited") must be zero. */ if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) { saddr6 = in6addr_linklocal_allnodes; saddr6.s6_addr16[1] = htons(ifp->if_index); nd6_na_output(ifp, &saddr6, &taddr6, ((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE) | (ip6_forwarding ? ND_NA_FLAG_ROUTER : 0), - tlladdr); - return; + tlladdr, (struct sockaddr *)proxydl); + goto freeit; } nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, ND_NEIGHBOR_SOLICIT, 0); nd6_na_output(ifp, &saddr6, &taddr6, ((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE) | (ip6_forwarding ? ND_NA_FLAG_ROUTER : 0) | ND_NA_FLAG_SOLICITED, - tlladdr); + tlladdr, (struct sockaddr *)proxydl); + freeit: + m_freem(m); return; bad: log(LOG_ERR, "nd6_ns_input: src=%s\n", ip6_sprintf(&saddr6)); log(LOG_ERR, "nd6_ns_input: dst=%s\n", ip6_sprintf(&daddr6)); log(LOG_ERR, "nd6_ns_input: tgt=%s\n", ip6_sprintf(&taddr6)); - return; + m_freem(m); } /* * Output an Neighbor Solicitation Message. Caller specifies: * - ICMP6 header source IP6 address * - ND6 header target IP6 address * - ND6 header source datalink address * * Based on RFC 2461 * Based on RFC 2462 (duplicated address detection) */ void nd6_ns_output(ifp, daddr6, taddr6, ln, dad) struct ifnet *ifp; struct in6_addr *daddr6, *taddr6; struct llinfo_nd6 *ln; /* for source address determination */ int dad; /* duplicated address detection */ { struct mbuf *m; struct ip6_hdr *ip6; struct nd_neighbor_solicit *nd_ns; struct in6_ifaddr *ia = NULL; struct ip6_moptions im6o; int icmp6len; + int maxlen; caddr_t mac; struct ifnet *outif = NULL; if (IN6_IS_ADDR_MULTICAST(taddr6)) return; - if ((m = m_gethdr(M_DONTWAIT, MT_DATA)) == NULL) + /* estimate the size of message */ + maxlen = sizeof(*ip6) + sizeof(*nd_ns); + maxlen += (sizeof(struct nd_opt_hdr) + ifp->if_addrlen + 7) & ~7; + if (max_linkhdr + maxlen >= MCLBYTES) { +#ifdef DIAGNOSTIC + printf("nd6_ns_output: max_linkhdr + maxlen >= MCLBYTES " + "(%d + %d > %d)\n", max_linkhdr, maxlen, MCLBYTES); +#endif return; + } + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m && max_linkhdr + maxlen >= MHLEN) { + MCLGET(m, M_DONTWAIT); + if ((m->m_flags & M_EXT) == 0) { + m_free(m); + m = NULL; + } + } + if (m == NULL) + return; + if (daddr6 == NULL || IN6_IS_ADDR_MULTICAST(daddr6)) { m->m_flags |= M_MCAST; im6o.im6o_multicast_ifp = ifp; im6o.im6o_multicast_hlim = 255; im6o.im6o_multicast_loop = 0; } icmp6len = sizeof(*nd_ns); m->m_pkthdr.len = m->m_len = sizeof(*ip6) + icmp6len; - MH_ALIGN(m, m->m_len + 16); /* 1+1+6 is enought. but just in case */ + m->m_data += max_linkhdr; /*or MH_ALIGN() equivalent?*/ /* fill neighbor solicitation packet */ ip6 = mtod(m, struct ip6_hdr *); ip6->ip6_flow = 0; - ip6->ip6_vfc = IPV6_VERSION; + ip6->ip6_vfc &= ~IPV6_VERSION_MASK; + ip6->ip6_vfc |= IPV6_VERSION; /* ip6->ip6_plen will be set later */ ip6->ip6_nxt = IPPROTO_ICMPV6; ip6->ip6_hlim = 255; if (daddr6) ip6->ip6_dst = *daddr6; else { ip6->ip6_dst.s6_addr16[0] = IPV6_ADDR_INT16_MLL; ip6->ip6_dst.s6_addr16[1] = htons(ifp->if_index); ip6->ip6_dst.s6_addr32[1] = 0; ip6->ip6_dst.s6_addr32[2] = IPV6_ADDR_INT32_ONE; ip6->ip6_dst.s6_addr32[3] = taddr6->s6_addr32[3]; ip6->ip6_dst.s6_addr8[12] = 0xff; } if (!dad) { - /* spec-wise correct, scope match */ +#if 0 /* KAME way, exact address scope match */ /* + * Select a source whose scope is the same as that of the dest. + * Typically, the dest is link-local solicitation multicast + * (i.e. neighbor discovery) or link-local/global unicast + * (i.e. neighbor un-reachability detection). + */ + ia = in6_ifawithifp(ifp, &ip6->ip6_dst); + if (ia == NULL) { + m_freem(m); + return; + } + ip6->ip6_src = ia->ia_addr.sin6_addr; +#else /* spec-wise correct */ + /* * RFC2461 7.2.2: * "If the source address of the packet prompting the * solicitation is the same as one of the addresses assigned * to the outgoing interface, that address SHOULD be placed * in the IP Source Address of the outgoing solicitation. * Otherwise, any one of the addresses assigned to the * interface should be used." * * We use the source address for the prompting packet * (saddr6), if: * - saddr6 is given from the caller (by giving "ln"), and * - saddr6 belongs to the outgoing interface. * Otherwise, we perform a scope-wise match. */ struct ip6_hdr *hip6; /*hold ip6*/ struct in6_addr *saddr6; if (ln && ln->ln_hold) { hip6 = mtod(ln->ln_hold, struct ip6_hdr *); /* XXX pullup? */ if (sizeof(*hip6) < ln->ln_hold->m_len) saddr6 = &hip6->ip6_src; else saddr6 = NULL; } else saddr6 = NULL; if (saddr6 && in6ifa_ifpwithaddr(ifp, saddr6)) bcopy(saddr6, &ip6->ip6_src, sizeof(*saddr6)); else { ia = in6_ifawithifp(ifp, &ip6->ip6_dst); if (ia == NULL) { m_freem(m); /*XXX*/ return; } ip6->ip6_src = ia->ia_addr.sin6_addr; } +#endif } else { /* * Source address for DAD packet must always be IPv6 * unspecified address. (0::0) */ bzero(&ip6->ip6_src, sizeof(ip6->ip6_src)); } nd_ns = (struct nd_neighbor_solicit *)(ip6 + 1); nd_ns->nd_ns_type = ND_NEIGHBOR_SOLICIT; nd_ns->nd_ns_code = 0; nd_ns->nd_ns_reserved = 0; nd_ns->nd_ns_target = *taddr6; if (IN6_IS_SCOPE_LINKLOCAL(&nd_ns->nd_ns_target)) nd_ns->nd_ns_target.s6_addr16[1] = 0; /* * Add source link-layer address option. * * spec implementation * --- --- * DAD packet MUST NOT do not add the option * there's no link layer address: * impossible do not add the option * there's link layer address: * Multicast NS MUST add one add the option * Unicast NS SHOULD add one add the option */ if (!dad && (mac = nd6_ifptomac(ifp))) { int optlen = sizeof(struct nd_opt_hdr) + ifp->if_addrlen; struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)(nd_ns + 1); /* 8 byte alignments... */ optlen = (optlen + 7) & ~7; m->m_pkthdr.len += optlen; m->m_len += optlen; icmp6len += optlen; bzero((caddr_t)nd_opt, optlen); nd_opt->nd_opt_type = ND_OPT_SOURCE_LINKADDR; nd_opt->nd_opt_len = optlen >> 3; bcopy(mac, (caddr_t)(nd_opt + 1), ifp->if_addrlen); } ip6->ip6_plen = htons((u_short)icmp6len); nd_ns->nd_ns_cksum = 0; nd_ns->nd_ns_cksum = in6_cksum(m, IPPROTO_ICMPV6, sizeof(*ip6), icmp6len); +#ifdef IPSEC + /* Don't lookup socket */ + ipsec_setsocket(m, NULL); +#endif ip6_output(m, NULL, NULL, dad ? IPV6_DADOUTPUT : 0, &im6o, &outif); if (outif) { icmp6_ifstat_inc(outif, ifs6_out_msg); icmp6_ifstat_inc(outif, ifs6_out_neighborsolicit); } icmp6stat.icp6s_outhist[ND_NEIGHBOR_SOLICIT]++; } /* * Neighbor advertisement input handling. * * Based on RFC 2461 * Based on RFC 2462 (duplicated address detection) + * + * the following items are not implemented yet: + * - proxy advertisement delay rule (RFC2461 7.2.8, last paragraph, SHOULD) + * - anycast advertisement delay rule (RFC2461 7.2.7, SHOULD) */ void nd6_na_input(m, off, icmp6len) struct mbuf *m; int off, icmp6len; { struct ifnet *ifp = m->m_pkthdr.rcvif; struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); - struct nd_neighbor_advert *nd_na - = (struct nd_neighbor_advert *)((caddr_t)ip6 + off); + struct nd_neighbor_advert *nd_na; +#if 0 + struct in6_addr saddr6 = ip6->ip6_src; +#endif struct in6_addr daddr6 = ip6->ip6_dst; - struct in6_addr taddr6 = nd_na->nd_na_target; - int flags = nd_na->nd_na_flags_reserved; - int is_router = ((flags & ND_NA_FLAG_ROUTER) != 0); - int is_solicited = ((flags & ND_NA_FLAG_SOLICITED) != 0); - int is_override = ((flags & ND_NA_FLAG_OVERRIDE) != 0); + struct in6_addr taddr6; + int flags; + int is_router; + int is_solicited; + int is_override; char *lladdr = NULL; int lladdrlen = 0; struct ifaddr *ifa; struct llinfo_nd6 *ln; struct rtentry *rt; struct sockaddr_dl *sdl; union nd_opts ndopts; if (ip6->ip6_hlim != 255) { log(LOG_ERR, "nd6_na_input: invalid hlim %d\n", ip6->ip6_hlim); + goto freeit; + } + +#ifndef PULLDOWN_TEST + IP6_EXTHDR_CHECK(m, off, icmp6len,); + nd_na = (struct nd_neighbor_advert *)((caddr_t)ip6 + off); +#else + IP6_EXTHDR_GET(nd_na, struct nd_neighbor_advert *, m, off, icmp6len); + if (nd_na == NULL) { + icmp6stat.icp6s_tooshort++; return; } +#endif + taddr6 = nd_na->nd_na_target; + flags = nd_na->nd_na_flags_reserved; + is_router = ((flags & ND_NA_FLAG_ROUTER) != 0); + is_solicited = ((flags & ND_NA_FLAG_SOLICITED) != 0); + is_override = ((flags & ND_NA_FLAG_OVERRIDE) != 0); if (IN6_IS_SCOPE_LINKLOCAL(&taddr6)) taddr6.s6_addr16[1] = htons(ifp->if_index); if (IN6_IS_ADDR_MULTICAST(&taddr6)) { log(LOG_ERR, "nd6_na_input: invalid target address %s\n", ip6_sprintf(&taddr6)); - return; + goto freeit; } if (IN6_IS_ADDR_MULTICAST(&daddr6)) if (is_solicited) { log(LOG_ERR, "nd6_na_input: a solicited adv is multicasted\n"); - return; + goto freeit; } icmp6len -= sizeof(*nd_na); nd6_option_init(nd_na + 1, icmp6len, &ndopts); if (nd6_options(&ndopts) < 0) { log(LOG_INFO, "nd6_na_input: invalid ND option, ignored\n"); - return; + goto freeit; } if (ndopts.nd_opts_tgt_lladdr) { lladdr = (char *)(ndopts.nd_opts_tgt_lladdr + 1); lladdrlen = ndopts.nd_opts_tgt_lladdr->nd_opt_len << 3; } ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &taddr6); /* * Target address matches one of my interface address. * * If my address is tentative, this means that there's somebody * already using the same address as mine. This indicates DAD failure. * This is defined in RFC 2462. * * Otherwise, process as defined in RFC 2461. */ if (ifa && (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_TENTATIVE)) { nd6_dad_na_input(ifa); - return; + goto freeit; } /* Just for safety, maybe unnecessery. */ if (ifa) { log(LOG_ERR, "nd6_na_input: duplicate IP6 address %s\n", ip6_sprintf(&taddr6)); - return; + goto freeit; } if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { log(LOG_INFO, "nd6_na_input: lladdrlen mismatch for %s " "(if %d, NA packet %d)\n", ip6_sprintf(&taddr6), ifp->if_addrlen, lladdrlen - 2); } /* * If no neighbor cache entry is found, NA SHOULD silently be discarded. */ rt = nd6_lookup(&taddr6, 0, ifp); if ((rt == NULL) || ((ln = (struct llinfo_nd6 *)rt->rt_llinfo) == NULL) || ((sdl = SDL(rt->rt_gateway)) == NULL)) - return; + goto freeit; if (ln->ln_state == ND6_LLINFO_INCOMPLETE) { /* * If the link-layer has address, and no lladdr option came, * discard the packet. */ if (ifp->if_addrlen && !lladdr) - return; + goto freeit; /* * Record link-layer address, and update the state. */ sdl->sdl_alen = ifp->if_addrlen; bcopy(lladdr, LLADDR(sdl), ifp->if_addrlen); if (is_solicited) { ln->ln_state = ND6_LLINFO_REACHABLE; + ln->ln_byhint = 0; if (ln->ln_expire) ln->ln_expire = time_second + nd_ifinfo[rt->rt_ifp->if_index].reachable; } else ln->ln_state = ND6_LLINFO_STALE; ln->ln_router = is_router; } else { int llchange; /* * Check if the link-layer address has changed or not. */ if (!lladdr) llchange = 0; else { if (sdl->sdl_alen) { if (bcmp(lladdr, LLADDR(sdl), ifp->if_addrlen)) llchange = 1; else llchange = 0; } else llchange = 1; } /* * This is VERY complex. Look at it with care. * * override solicit lladdr llchange action * (L: record lladdr) * * 0 0 n -- (2c) * 0 0 y n (2b) L * 0 0 y y (1) REACHABLE->STALE * 0 1 n -- (2c) *->REACHABLE * 0 1 y n (2b) L *->REACHABLE * 0 1 y y (1) REACHABLE->STALE * 1 0 n -- (2a) * 1 0 y n (2a) L * 1 0 y y (2a) L *->STALE * 1 1 n -- (2a) *->REACHABLE * 1 1 y n (2a) L *->REACHABLE * 1 1 y y (2a) L *->REACHABLE */ if (!is_override && (lladdr && llchange)) { /* (1) */ /* * If state is REACHABLE, make it STALE. * no other updates should be done. */ if (ln->ln_state == ND6_LLINFO_REACHABLE) ln->ln_state = ND6_LLINFO_STALE; - return; + goto freeit; } else if (is_override /* (2a) */ || (!is_override && (lladdr && !llchange)) /* (2b) */ || !lladdr) { /* (2c) */ /* * Update link-local address, if any. */ if (lladdr) { sdl->sdl_alen = ifp->if_addrlen; bcopy(lladdr, LLADDR(sdl), ifp->if_addrlen); } /* * If solicited, make the state REACHABLE. * If not solicited and the link-layer address was * changed, make it STALE. */ if (is_solicited) { ln->ln_state = ND6_LLINFO_REACHABLE; + ln->ln_byhint = 0; if (ln->ln_expire) { ln->ln_expire = time_second + nd_ifinfo[ifp->if_index].reachable; } } else { if (lladdr && llchange) ln->ln_state = ND6_LLINFO_STALE; } } if (ln->ln_router && !is_router) { /* * The peer dropped the router flag. * Remove the sender from the Default Router List and * update the Destination Cache entries. */ struct nd_defrouter *dr; struct in6_addr *in6; int s; in6 = &((struct sockaddr_in6 *)rt_key(rt))->sin6_addr; s = splnet(); dr = defrouter_lookup(in6, rt->rt_ifp); if (dr) defrtrlist_del(dr); else if (!ip6_forwarding && ip6_accept_rtadv) { /* * Even if the neighbor is not in the default * router list, the neighbor may be used * as a next hop for some destinations * (e.g. redirect case). So we must * call rt6_flush explicitly. */ rt6_flush(&ip6->ip6_src, rt->rt_ifp); } splx(s); } ln->ln_router = is_router; } rt->rt_flags &= ~RTF_REJECT; ln->ln_asked = 0; if (ln->ln_hold) { - nd6_output(ifp, ln->ln_hold, +#ifdef OLDIP6OUTPUT + (*ifp->if_output)(ifp, ln->ln_hold, rt_key(rt), rt); +#else + /* + * we assume ifp is not a p2p here, so just set the 2nd + * argument as the 1st one. + */ + nd6_output(ifp, ifp, ln->ln_hold, (struct sockaddr_in6 *)rt_key(rt), rt); +#endif ln->ln_hold = 0; } + + freeit: + m_freem(m); } /* * Neighbor advertisement output handling. * * Based on RFC 2461 * - * XXX NA delay for anycast address is not implemented yet - * (RFC 2461 7.2.7) - * XXX proxy advertisement? + * the following items are not implemented yet: + * - proxy advertisement delay rule (RFC2461 7.2.8, last paragraph, SHOULD) + * - anycast advertisement delay rule (RFC2461 7.2.7, SHOULD) */ void -nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr) +nd6_na_output(ifp, daddr6, taddr6, flags, tlladdr, sdl0) struct ifnet *ifp; struct in6_addr *daddr6, *taddr6; u_long flags; - int tlladdr; /* 1 if include target link-layer address */ + int tlladdr; /* 1 if include target link-layer address */ + struct sockaddr *sdl0; /* sockaddr_dl (= proxy NA) or NULL */ { struct mbuf *m; struct ip6_hdr *ip6; struct nd_neighbor_advert *nd_na; struct in6_ifaddr *ia = NULL; struct ip6_moptions im6o; int icmp6len; + int maxlen; caddr_t mac; - struct ifnet *outif; - - if ((m = m_gethdr(M_DONTWAIT, MT_DATA)) == NULL) + struct ifnet *outif = NULL; + + /* estimate the size of message */ + maxlen = sizeof(*ip6) + sizeof(*nd_na); + maxlen += (sizeof(struct nd_opt_hdr) + ifp->if_addrlen + 7) & ~7; + if (max_linkhdr + maxlen >= MCLBYTES) { +#ifdef DIAGNOSTIC + printf("nd6_na_output: max_linkhdr + maxlen >= MCLBYTES " + "(%d + %d > %d)\n", max_linkhdr, maxlen, MCLBYTES); +#endif return; + } + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m && max_linkhdr + maxlen >= MHLEN) { + MCLGET(m, M_DONTWAIT); + if ((m->m_flags & M_EXT) == 0) { + m_free(m); + m = NULL; + } + } + if (m == NULL) + return; + if (IN6_IS_ADDR_MULTICAST(daddr6)) { m->m_flags |= M_MCAST; im6o.im6o_multicast_ifp = ifp; im6o.im6o_multicast_hlim = 255; im6o.im6o_multicast_loop = 0; } icmp6len = sizeof(*nd_na); m->m_pkthdr.len = m->m_len = sizeof(struct ip6_hdr) + icmp6len; - MH_ALIGN(m, m->m_len + 16); /* 1+1+6 is enough. but just in case */ + m->m_data += max_linkhdr; /*or MH_ALIGN() equivalent?*/ /* fill neighbor advertisement packet */ ip6 = mtod(m, struct ip6_hdr *); ip6->ip6_flow = 0; - ip6->ip6_vfc = IPV6_VERSION; + ip6->ip6_vfc &= ~IPV6_VERSION_MASK; + ip6->ip6_vfc |= IPV6_VERSION; ip6->ip6_nxt = IPPROTO_ICMPV6; ip6->ip6_hlim = 255; if (IN6_IS_ADDR_UNSPECIFIED(daddr6)) { /* reply to DAD */ ip6->ip6_dst.s6_addr16[0] = IPV6_ADDR_INT16_MLL; ip6->ip6_dst.s6_addr16[1] = htons(ifp->if_index); ip6->ip6_dst.s6_addr32[1] = 0; ip6->ip6_dst.s6_addr32[2] = 0; ip6->ip6_dst.s6_addr32[3] = IPV6_ADDR_INT32_ONE; flags &= ~ND_NA_FLAG_SOLICITED; } else ip6->ip6_dst = *daddr6; /* * Select a source whose scope is the same as that of the dest. */ ia = in6_ifawithifp(ifp, &ip6->ip6_dst); if (ia == NULL) { m_freem(m); return; } ip6->ip6_src = ia->ia_addr.sin6_addr; nd_na = (struct nd_neighbor_advert *)(ip6 + 1); nd_na->nd_na_type = ND_NEIGHBOR_ADVERT; nd_na->nd_na_code = 0; nd_na->nd_na_target = *taddr6; if (IN6_IS_SCOPE_LINKLOCAL(&nd_na->nd_na_target)) nd_na->nd_na_target.s6_addr16[1] = 0; /* * "tlladdr" indicates NS's condition for adding tlladdr or not. * see nd6_ns_input() for details. * Basically, if NS packet is sent to unicast/anycast addr, * target lladdr option SHOULD NOT be included. */ - if (tlladdr && (mac = nd6_ifptomac(ifp))) { + if (tlladdr) { + mac = NULL; + /* + * sdl0 != NULL indicates proxy NA. If we do proxy, use + * lladdr in sdl0. If we are not proxying (sending NA for + * my address) use lladdr configured for the interface. + */ + if (sdl0 == NULL) + mac = nd6_ifptomac(ifp); + else if (sdl0->sa_family == AF_LINK) { + struct sockaddr_dl *sdl; + sdl = (struct sockaddr_dl *)sdl0; + if (sdl->sdl_alen == ifp->if_addrlen) + mac = LLADDR(sdl); + } + } + if (tlladdr && mac) { int optlen = sizeof(struct nd_opt_hdr) + ifp->if_addrlen; struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)(nd_na + 1); /* roundup to 8 bytes alignment! */ optlen = (optlen + 7) & ~7; m->m_pkthdr.len += optlen; m->m_len += optlen; icmp6len += optlen; bzero((caddr_t)nd_opt, optlen); nd_opt->nd_opt_type = ND_OPT_TARGET_LINKADDR; nd_opt->nd_opt_len = optlen >> 3; bcopy(mac, (caddr_t)(nd_opt + 1), ifp->if_addrlen); } else flags &= ~ND_NA_FLAG_OVERRIDE; ip6->ip6_plen = htons((u_short)icmp6len); nd_na->nd_na_flags_reserved = flags; nd_na->nd_na_cksum = 0; nd_na->nd_na_cksum = in6_cksum(m, IPPROTO_ICMPV6, sizeof(struct ip6_hdr), icmp6len); #ifdef IPSEC - m->m_pkthdr.rcvif = NULL; -#endif /*IPSEC*/ + /* Don't lookup socket */ + ipsec_setsocket(m, NULL); +#endif ip6_output(m, NULL, NULL, 0, &im6o, &outif); if (outif) { icmp6_ifstat_inc(outif, ifs6_out_msg); icmp6_ifstat_inc(outif, ifs6_out_neighboradvert); } icmp6stat.icp6s_outhist[ND_NEIGHBOR_ADVERT]++; } caddr_t nd6_ifptomac(ifp) struct ifnet *ifp; { switch (ifp->if_type) { case IFT_ARCNET: case IFT_ETHER: case IFT_FDDI: return ((caddr_t)(ifp + 1)); break; default: return NULL; } } TAILQ_HEAD(dadq_head, dadq); struct dadq { TAILQ_ENTRY(dadq) dad_list; struct ifaddr *dad_ifa; int dad_count; /* max NS to send */ + int dad_ns_tcount; /* # of trials to send NS */ int dad_ns_ocount; /* NS sent so far */ int dad_ns_icount; int dad_na_icount; struct callout_handle dad_timer; }; static struct dadq_head dadq; static struct dadq * nd6_dad_find(ifa) struct ifaddr *ifa; { struct dadq *dp; - TAILQ_FOREACH(dp, &dadq, dad_list) { + for (dp = dadq.tqh_first; dp; dp = dp->dad_list.tqe_next) { if (dp->dad_ifa == ifa) return dp; } return NULL; } /* * Start Duplicated Address Detection (DAD) for specified interface address. */ void nd6_dad_start(ifa, tick) struct ifaddr *ifa; int *tick; /* minimum delay ticks for IFF_UP event */ { struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa; struct dadq *dp; static int dad_init = 0; if (!dad_init) { TAILQ_INIT(&dadq); dad_init++; } /* * If we don't need DAD, don't do it. * There are several cases: * - DAD is disabled (ip6_dad_count == 0) * - the interface address is anycast */ if (!(ia->ia6_flags & IN6_IFF_TENTATIVE)) { - printf("nd6_dad_start: called with non-tentative address " + log(LOG_DEBUG, + "nd6_dad_start: called with non-tentative address " "%s(%s)\n", ip6_sprintf(&ia->ia_addr.sin6_addr), ifa->ifa_ifp ? if_name(ifa->ifa_ifp) : "???"); return; } if (ia->ia6_flags & IN6_IFF_ANYCAST) { ia->ia6_flags &= ~IN6_IFF_TENTATIVE; return; } if (!ip6_dad_count) { ia->ia6_flags &= ~IN6_IFF_TENTATIVE; return; } if (!ifa->ifa_ifp) panic("nd6_dad_start: ifa->ifa_ifp == NULL"); if (!(ifa->ifa_ifp->if_flags & IFF_UP)) return; if (nd6_dad_find(ifa) != NULL) { /* DAD already in progress */ return; } dp = malloc(sizeof(*dp), M_IP6NDP, M_NOWAIT); if (dp == NULL) { - printf("nd6_dad_start: memory allocation failed for " + log(LOG_ERR, "nd6_dad_start: memory allocation failed for " "%s(%s)\n", ip6_sprintf(&ia->ia_addr.sin6_addr), ifa->ifa_ifp ? if_name(ifa->ifa_ifp) : "???"); return; } bzero(dp, sizeof(*dp)); TAILQ_INSERT_TAIL(&dadq, (struct dadq *)dp, dad_list); #ifdef ND6_DEBUG - printf("%s: starting DAD for %s\n", if_name(ifa->ifa_ifp), + log(LOG_DEBUG, "%s: starting DAD for %s\n", if_name(ifa->ifa_ifp), ip6_sprintf(&ia->ia_addr.sin6_addr)); #endif /* * Send NS packet for DAD, ip6_dad_count times. * Note that we must delay the first transmission, if this is the * first packet to be sent from the interface after interface * (re)initialization. */ dp->dad_ifa = ifa; ifa->ifa_refcnt++; /*just for safety*/ dp->dad_count = ip6_dad_count; dp->dad_ns_icount = dp->dad_na_icount = 0; - dp->dad_ns_ocount = 0; + dp->dad_ns_ocount = dp->dad_ns_tcount = 0; if (!tick) { - dp->dad_ns_ocount++; - nd6_ns_output(ifa->ifa_ifp, NULL, &ia->ia_addr.sin6_addr, - NULL, 1); + nd6_dad_ns_output(dp, ifa); dp->dad_timer = timeout((void (*) __P((void *)))nd6_dad_timer, (void *)ifa, nd_ifinfo[ifa->ifa_ifp->if_index].retrans * hz / 1000); } else { int ntick; if (*tick == 0) ntick = random() % (MAX_RTR_SOLICITATION_DELAY * hz); else ntick = *tick + random() % (hz / 2); *tick = ntick; dp->dad_timer = timeout((void (*) __P((void *)))nd6_dad_timer, (void *)ifa, ntick); } } static void nd6_dad_timer(ifa) struct ifaddr *ifa; { int s; struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa; struct dadq *dp; s = splnet(); /*XXX*/ /* Sanity check */ if (ia == NULL) { - printf("nd6_dad_timer: called with null parameter\n"); + log(LOG_ERR, "nd6_dad_timer: called with null parameter\n"); goto done; } dp = nd6_dad_find(ifa); if (dp == NULL) { - printf("nd6_dad_timer: DAD structure not found\n"); + log(LOG_ERR, "nd6_dad_timer: DAD structure not found\n"); goto done; } if (ia->ia6_flags & IN6_IFF_DUPLICATED) { - printf("nd6_dad_timer: called with duplicated address " + log(LOG_ERR, "nd6_dad_timer: called with duplicated address " "%s(%s)\n", ip6_sprintf(&ia->ia_addr.sin6_addr), ifa->ifa_ifp ? if_name(ifa->ifa_ifp) : "???"); goto done; } if ((ia->ia6_flags & IN6_IFF_TENTATIVE) == 0) { - printf("nd6_dad_timer: called with non-tentative address " + log(LOG_ERR, "nd6_dad_timer: called with non-tentative address " "%s(%s)\n", ip6_sprintf(&ia->ia_addr.sin6_addr), ifa->ifa_ifp ? if_name(ifa->ifa_ifp) : "???"); goto done; } + /* timeouted with IFF_{RUNNING,UP} check */ + if (dp->dad_ns_tcount > dad_maxtry) { + log(LOG_ERR, "%s: could not run DAD, driver problem?\n", + if_name(ifa->ifa_ifp)); + + TAILQ_REMOVE(&dadq, (struct dadq *)dp, dad_list); + free(dp, M_IP6NDP); + dp = NULL; + IFAFREE(ifa); + goto done; + } + /* Need more checks? */ if (dp->dad_ns_ocount < dp->dad_count) { /* * We have more NS to go. Send NS packet for DAD. */ - dp->dad_ns_ocount++; - nd6_ns_output(ifa->ifa_ifp, NULL, &ia->ia_addr.sin6_addr, - NULL, 1); + nd6_dad_ns_output(dp, ifa); dp->dad_timer = timeout((void (*) __P((void *)))nd6_dad_timer, (void *)ifa, nd_ifinfo[ifa->ifa_ifp->if_index].retrans * hz / 1000); } else { /* * We have transmitted sufficient number of DAD packets. * See what we've got. */ int duplicate; duplicate = 0; if (dp->dad_na_icount) { /* * the check is in nd6_dad_na_input(), * but just in case */ duplicate++; } if (dp->dad_ns_icount) { +#if 0 /*heuristics*/ + /* + * if + * - we have sent many(?) DAD NS, and + * - the number of NS we sent equals to the + * number of NS we've got, and + * - we've got no NA + * we may have a faulty network card/driver which + * loops back multicasts to myself. + */ + if (3 < dp->dad_count + && dp->dad_ns_icount == dp->dad_count + && dp->dad_na_icount == 0) { + log(LOG_INFO, "DAD questionable for %s(%s): " + "network card loops back multicast?\n", + ip6_sprintf(&ia->ia_addr.sin6_addr), + if_name(ifa->ifa_ifp)); + /* XXX consider it a duplicate or not? */ + /* duplicate++; */ + } else { + /* We've seen NS, means DAD has failed. */ + duplicate++; + } +#else /* We've seen NS, means DAD has failed. */ duplicate++; +#endif } if (duplicate) { /* (*dp) will be freed in nd6_dad_duplicated() */ dp = NULL; nd6_dad_duplicated(ifa); } else { /* * We are done with DAD. No NA came, no NS came. * duplicated address found. */ ia->ia6_flags &= ~IN6_IFF_TENTATIVE; #ifdef ND6_DEBUG - printf("%s: DAD complete for %s - no duplicates " - "found\n", if_name(ifa->ifa_ifp), + log(LOG_INFO, + "%s: DAD complete for %s - no duplicates found\n", + if_name(ifa->ifa_ifp), ip6_sprintf(&ia->ia_addr.sin6_addr)); #endif TAILQ_REMOVE(&dadq, (struct dadq *)dp, dad_list); free(dp, M_IP6NDP); dp = NULL; - ifa->ifa_refcnt--; + IFAFREE(ifa); } } done: splx(s); } void nd6_dad_duplicated(ifa) struct ifaddr *ifa; { struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa; struct dadq *dp; dp = nd6_dad_find(ifa); if (dp == NULL) { - printf("nd6_dad_duplicated: DAD structure not found\n"); + log(LOG_ERR, "nd6_dad_duplicated: DAD structure not found\n"); return; } log(LOG_ERR, "%s: DAD detected duplicate IPv6 address %s: %d NS, " "%d NA\n", if_name(ifa->ifa_ifp), ip6_sprintf(&ia->ia_addr.sin6_addr), dp->dad_ns_icount, dp->dad_na_icount); ia->ia6_flags &= ~IN6_IFF_TENTATIVE; ia->ia6_flags |= IN6_IFF_DUPLICATED; /* We are done with DAD, with duplicated address found. (failure) */ untimeout((void (*) __P((void *)))nd6_dad_timer, (void *)ifa - , dp->dad_timer); + , dp->dad_timer + ); - printf("%s: DAD complete for %s - duplicate found\n", + log(LOG_ERR, "%s: DAD complete for %s - duplicate found\n", if_name(ifa->ifa_ifp), ip6_sprintf(&ia->ia_addr.sin6_addr)); - printf("%s: manual intervention required\n", if_name(ifa->ifa_ifp)); + log(LOG_ERR, "%s: manual intervention required\n", + if_name(ifa->ifa_ifp)); TAILQ_REMOVE(&dadq, (struct dadq *)dp, dad_list); free(dp, M_IP6NDP); dp = NULL; - ifa->ifa_refcnt--; + IFAFREE(ifa); } -void +static void +nd6_dad_ns_output(dp, ifa) + struct dadq *dp; + struct ifaddr *ifa; +{ + struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa; + struct ifnet *ifp = ifa->ifa_ifp; + + dp->dad_ns_tcount++; + if ((ifp->if_flags & IFF_UP) == 0) { +#if 0 + printf("%s: interface down?\n", if_name(ifp)); +#endif + return; + } + if ((ifp->if_flags & IFF_RUNNING) == 0) { +#if 0 + printf("%s: interface not running?\n", if_name(ifp)); +#endif + return; + } + + dp->dad_ns_ocount++; + nd6_ns_output(ifp, NULL, &ia->ia_addr.sin6_addr, NULL, 1); +} + +static void nd6_dad_ns_input(ifa) struct ifaddr *ifa; { struct in6_ifaddr *ia; struct ifnet *ifp; struct in6_addr *taddr6; struct dadq *dp; int duplicate; if (!ifa) panic("ifa == NULL in nd6_dad_ns_input"); ia = (struct in6_ifaddr *)ifa; ifp = ifa->ifa_ifp; taddr6 = &ia->ia_addr.sin6_addr; duplicate = 0; dp = nd6_dad_find(ifa); /* * If it is from myself, ignore this. */ if (ifp && (ifp->if_flags & IFF_LOOPBACK)) return; /* Quickhack - completely ignore DAD NS packets */ if (dad_ignore_ns) { log(LOG_INFO, "nd6_dad_ns_input: ignoring DAD NS packet for " "address %s(%s)\n", ip6_sprintf(taddr6), if_name(ifa->ifa_ifp)); return; } /* * if I'm yet to start DAD, someone else started using this address * first. I have a duplicate and you win. */ if (!dp || dp->dad_ns_ocount == 0) duplicate++; /* XXX more checks for loopback situation - see nd6_dad_timer too */ if (duplicate) { dp = NULL; /* will be freed in nd6_dad_duplicated() */ nd6_dad_duplicated(ifa); } else { /* * not sure if I got a duplicate. * increment ns count and see what happens. */ if (dp) dp->dad_ns_icount++; } } -void +static void nd6_dad_na_input(ifa) struct ifaddr *ifa; { struct dadq *dp; if (!ifa) panic("ifa == NULL in nd6_dad_na_input"); dp = nd6_dad_find(ifa); if (dp) dp->dad_na_icount++; /* remove the address. */ nd6_dad_duplicated(ifa); } Index: head/sys/netinet6/nd6_rtr.c =================================================================== --- head/sys/netinet6/nd6_rtr.c (revision 62586) +++ head/sys/netinet6/nd6_rtr.c (revision 62587) @@ -1,1242 +1,1601 @@ +/* $FreeBSD$ */ +/* $KAME: nd6_rtr.c,v 1.43 2000/07/02 23:19:59 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ +#include "opt_inet.h" +#include "opt_inet6.h" + #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include -#include +#include +#include #include -#define SDL(s) ((struct sockaddr_dl *)s) +#define SDL(s) ((struct sockaddr_dl *)s) -static struct nd_defrouter *defrtrlist_update __P((struct nd_defrouter *)); -static int prelist_add __P((struct nd_prefix *, struct nd_defrouter *)); -static struct nd_prefix *prefix_lookup __P((struct nd_prefix *)); -static struct in6_ifaddr *in6_ifadd __P((struct ifnet *, struct in6_addr *, - struct in6_addr *, int)); -static struct nd_pfxrouter *pfxrtr_lookup __P((struct nd_prefix *, - struct nd_defrouter *)); -static void pfxrtr_add __P((struct nd_prefix *, struct nd_defrouter *)); -static void pfxrtr_del __P((struct nd_pfxrouter *)); -static void pfxlist_onlink_check __P((void)); -static void nd6_detach_prefix __P((struct nd_prefix *)); -static void nd6_attach_prefix __P((struct nd_prefix *)); +static struct nd_defrouter *defrtrlist_update __P((struct nd_defrouter *)); +static int prelist_add __P((struct nd_prefix *, struct nd_defrouter *)); +static struct nd_prefix *prefix_lookup __P((struct nd_prefix *)); +static struct in6_ifaddr *in6_ifadd __P((struct ifnet *, struct in6_addr *, + struct in6_addr *, int)); +static struct nd_pfxrouter *pfxrtr_lookup __P((struct nd_prefix *, + struct nd_defrouter *)); +static void pfxrtr_add __P((struct nd_prefix *, struct nd_defrouter *)); +static void pfxrtr_del __P((struct nd_pfxrouter *)); +static struct nd_pfxrouter *find_pfxlist_reachable_router + __P((struct nd_prefix *)); +static void nd6_detach_prefix __P((struct nd_prefix *)); +static void nd6_attach_prefix __P((struct nd_prefix *)); +static void defrouter_addifreq __P((struct ifnet *)); +#ifdef ND6_USE_RTSOCK +static void defrouter_msg __P((int, struct rtentry *)); +#endif -static void in6_init_address_ltimes __P((struct nd_prefix *ndpr, - struct in6_addrlifetime *lt6)); +static void in6_init_address_ltimes __P((struct nd_prefix *ndpr, + struct in6_addrlifetime *lt6, + int update_vltime)); -static int rt6_deleteroute __P((struct radix_node *, void *)); +static int rt6_deleteroute __P((struct radix_node *, void *)); -extern int nd6_recalc_reachtm_interval; +extern int nd6_recalc_reachtm_interval; +struct ifnet *nd6_defifp; +int nd6_defifindex; + /* * Receive Router Solicitation Message - just for routers. * Router solicitation/advertisement is mostly managed by userland program * (rtadvd) so here we have no function like nd6_ra_output(). * * Based on RFC 2461 */ void nd6_rs_input(m, off, icmp6len) struct mbuf *m; int off, icmp6len; { struct ifnet *ifp = m->m_pkthdr.rcvif; struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); - struct nd_router_solicit *nd_rs - = (struct nd_router_solicit *)((caddr_t)ip6 + off); + struct nd_router_solicit *nd_rs; struct in6_addr saddr6 = ip6->ip6_src; +#if 0 + struct in6_addr daddr6 = ip6->ip6_dst; +#endif char *lladdr = NULL; int lladdrlen = 0; +#if 0 + struct sockaddr_dl *sdl = (struct sockaddr_dl *)NULL; + struct llinfo_nd6 *ln = (struct llinfo_nd6 *)NULL; + struct rtentry *rt = NULL; + int is_newentry; +#endif union nd_opts ndopts; /* If I'm not a router, ignore it. */ if (ip6_accept_rtadv != 0 || ip6_forwarding != 1) - return; + goto freeit; /* Sanity checks */ if (ip6->ip6_hlim != 255) { log(LOG_ERR, "nd6_rs_input: invalid hlim %d\n", ip6->ip6_hlim); - return; + goto freeit; } /* * Don't update the neighbor cache, if src = ::. * This indicates that the src has no IP address assigned yet. */ if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) + goto freeit; + +#ifndef PULLDOWN_TEST + IP6_EXTHDR_CHECK(m, off, icmp6len,); + nd_rs = (struct nd_router_solicit *)((caddr_t)ip6 + off); +#else + IP6_EXTHDR_GET(nd_rs, struct nd_router_solicit *, m, off, icmp6len); + if (nd_rs == NULL) { + icmp6stat.icp6s_tooshort++; return; + } +#endif icmp6len -= sizeof(*nd_rs); nd6_option_init(nd_rs + 1, icmp6len, &ndopts); if (nd6_options(&ndopts) < 0) { log(LOG_INFO, "nd6_rs_input: invalid ND option, ignored\n"); - return; + goto freeit; } if (ndopts.nd_opts_src_lladdr) { lladdr = (char *)(ndopts.nd_opts_src_lladdr + 1); lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3; } if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { log(LOG_INFO, "nd6_rs_input: lladdrlen mismatch for %s " "(if %d, RS packet %d)\n", ip6_sprintf(&saddr6), ifp->if_addrlen, lladdrlen - 2); } nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, ND_ROUTER_SOLICIT, 0); + + freeit: + m_freem(m); } /* * Receive Router Advertisement Message. * * Based on RFC 2461 * TODO: on-link bit on prefix information * TODO: ND_RA_FLAG_{OTHER,MANAGED} processing */ void nd6_ra_input(m, off, icmp6len) struct mbuf *m; int off, icmp6len; { struct ifnet *ifp = m->m_pkthdr.rcvif; struct nd_ifinfo *ndi = &nd_ifinfo[ifp->if_index]; struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); - struct nd_router_advert *nd_ra = - (struct nd_router_advert *)((caddr_t)ip6 + off); + struct nd_router_advert *nd_ra; struct in6_addr saddr6 = ip6->ip6_src; +#if 0 + struct in6_addr daddr6 = ip6->ip6_dst; + int flags; /* = nd_ra->nd_ra_flags_reserved; */ + int is_managed = ((flags & ND_RA_FLAG_MANAGED) != 0); + int is_other = ((flags & ND_RA_FLAG_OTHER) != 0); +#endif union nd_opts ndopts; struct nd_defrouter *dr; if (ip6_accept_rtadv == 0) - return; + goto freeit; if (ip6->ip6_hlim != 255) { log(LOG_ERR, "nd6_ra_input: invalid hlim %d\n", ip6->ip6_hlim); - return; + goto freeit; } if (!IN6_IS_ADDR_LINKLOCAL(&saddr6)) { log(LOG_ERR, "nd6_ra_input: src %s is not link-local\n", ip6_sprintf(&saddr6)); + goto freeit; + } + +#ifndef PULLDOWN_TEST + IP6_EXTHDR_CHECK(m, off, icmp6len,); + nd_ra = (struct nd_router_advert *)((caddr_t)ip6 + off); +#else + IP6_EXTHDR_GET(nd_ra, struct nd_router_advert *, m, off, icmp6len); + if (nd_ra == NULL) { + icmp6stat.icp6s_tooshort++; return; } +#endif icmp6len -= sizeof(*nd_ra); nd6_option_init(nd_ra + 1, icmp6len, &ndopts); if (nd6_options(&ndopts) < 0) { log(LOG_INFO, "nd6_ra_input: invalid ND option, ignored\n"); - return; + goto freeit; } { struct nd_defrouter dr0; u_int32_t advreachable = nd_ra->nd_ra_reachable; dr0.rtaddr = saddr6; dr0.flags = nd_ra->nd_ra_flags_reserved; dr0.rtlifetime = ntohs(nd_ra->nd_ra_router_lifetime); dr0.expire = time_second + dr0.rtlifetime; dr0.ifp = ifp; + dr0.advint = 0; /* Mobile IPv6 */ + dr0.advint_expire = 0; /* Mobile IPv6 */ + dr0.advints_lost = 0; /* Mobile IPv6 */ /* unspecified or not? (RFC 2461 6.3.4) */ if (advreachable) { NTOHL(advreachable); if (advreachable <= MAX_REACHABLE_TIME && ndi->basereachable != advreachable) { ndi->basereachable = advreachable; ndi->reachable = ND_COMPUTE_RTIME(ndi->basereachable); ndi->recalctm = nd6_recalc_reachtm_interval; /* reset */ } } if (nd_ra->nd_ra_retransmit) ndi->retrans = ntohl(nd_ra->nd_ra_retransmit); if (nd_ra->nd_ra_curhoplimit) ndi->chlim = nd_ra->nd_ra_curhoplimit; dr = defrtrlist_update(&dr0); } /* * prefix */ if (ndopts.nd_opts_pi) { struct nd_opt_hdr *pt; struct nd_opt_prefix_info *pi; struct nd_prefix pr; for (pt = (struct nd_opt_hdr *)ndopts.nd_opts_pi; pt <= (struct nd_opt_hdr *)ndopts.nd_opts_pi_end; pt = (struct nd_opt_hdr *)((caddr_t)pt + (pt->nd_opt_len << 3))) { if (pt->nd_opt_type != ND_OPT_PREFIX_INFORMATION) continue; pi = (struct nd_opt_prefix_info *)pt; if (pi->nd_opt_pi_len != 4) { log(LOG_INFO, "nd6_ra_input: invalid option " "len %d for prefix information option, " "ignored\n", pi->nd_opt_pi_len); continue; } if (128 < pi->nd_opt_pi_prefix_len) { log(LOG_INFO, "nd6_ra_input: invalid prefix " "len %d for prefix information option, " "ignored\n", pi->nd_opt_pi_prefix_len); continue; } if (IN6_IS_ADDR_MULTICAST(&pi->nd_opt_pi_prefix) || IN6_IS_ADDR_LINKLOCAL(&pi->nd_opt_pi_prefix)) { log(LOG_INFO, "nd6_ra_input: invalid prefix " "%s, ignored\n", ip6_sprintf(&pi->nd_opt_pi_prefix)); continue; } /* aggregatable unicast address, rfc2374 */ if ((pi->nd_opt_pi_prefix.s6_addr8[0] & 0xe0) == 0x20 && pi->nd_opt_pi_prefix_len != 64) { log(LOG_INFO, "nd6_ra_input: invalid prefixlen " "%d for rfc2374 prefix %s, ignored\n", pi->nd_opt_pi_prefix_len, ip6_sprintf(&pi->nd_opt_pi_prefix)); continue; } bzero(&pr, sizeof(pr)); pr.ndpr_prefix.sin6_family = AF_INET6; pr.ndpr_prefix.sin6_len = sizeof(pr.ndpr_prefix); pr.ndpr_prefix.sin6_addr = pi->nd_opt_pi_prefix; pr.ndpr_ifp = (struct ifnet *)m->m_pkthdr.rcvif; pr.ndpr_raf_onlink = (pi->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK) ? 1 : 0; pr.ndpr_raf_auto = (pi->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_AUTO) ? 1 : 0; pr.ndpr_plen = pi->nd_opt_pi_prefix_len; pr.ndpr_vltime = ntohl(pi->nd_opt_pi_valid_time); pr.ndpr_pltime = ntohl(pi->nd_opt_pi_preferred_time); if (in6_init_prefix_ltimes(&pr)) continue; /* prefix lifetime init failed */ (void)prelist_update(&pr, dr, m); } } /* * MTU */ if (ndopts.nd_opts_mtu && ndopts.nd_opts_mtu->nd_opt_mtu_len == 1) { u_int32_t mtu = ntohl(ndopts.nd_opts_mtu->nd_opt_mtu_mtu); /* lower bound */ if (mtu < IPV6_MMTU) { log(LOG_INFO, "nd6_ra_input: bogus mtu option " "mtu=%d sent from %s, ignoring\n", mtu, ip6_sprintf(&ip6->ip6_src)); goto skip; } /* upper bound */ if (ndi->maxmtu) { if (mtu <= ndi->maxmtu) { int change = (ndi->linkmtu != mtu); ndi->linkmtu = mtu; if (change) /* in6_maxmtu may change */ in6_setmaxmtu(); } else { log(LOG_INFO, "nd6_ra_input: bogus mtu " "mtu=%d sent from %s; " "exceeds maxmtu %d, ignoring\n", mtu, ip6_sprintf(&ip6->ip6_src), ndi->maxmtu); } } else { log(LOG_INFO, "nd6_ra_input: mtu option " "mtu=%d sent from %s; maxmtu unknown, " "ignoring\n", mtu, ip6_sprintf(&ip6->ip6_src)); } } skip: /* * Src linkaddress */ { char *lladdr = NULL; int lladdrlen = 0; if (ndopts.nd_opts_src_lladdr) { lladdr = (char *)(ndopts.nd_opts_src_lladdr + 1); lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3; } if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { log(LOG_INFO, "nd6_ra_input: lladdrlen mismatch for %s " "(if %d, RA packet %d)\n", ip6_sprintf(&saddr6), ifp->if_addrlen, lladdrlen - 2); } nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, ND_ROUTER_ADVERT, 0); + + /* + * Installing a link-layer address might change the state of the + * router's neighbor cache, which might also affect our on-link + * detection of adveritsed prefixes. + */ + pfxlist_onlink_check(); } + +freeit: + m_freem(m); } /* * default router list proccessing sub routines */ + +#ifdef ND6_USE_RTSOCK +/* tell the change to user processes watching the routing socket. */ +static void +defrouter_msg(cmd, rt) + int cmd; + struct rtentry *rt; +{ + struct rt_addrinfo info; + + bzero((caddr_t)&info, sizeof(info)); + info.rti_info[RTAX_DST] = rt_key(rt); + info.rti_info[RTAX_GATEWAY] = rt->rt_gateway; + info.rti_info[RTAX_NETMASK] = rt_mask(rt); + + rt_missmsg(cmd, &info, rt->rt_flags, 0); +} +#endif + void defrouter_addreq(new) struct nd_defrouter *new; { struct sockaddr_in6 def, mask, gate; + struct rtentry *newrt = NULL; int s; Bzero(&def, sizeof(def)); Bzero(&mask, sizeof(mask)); Bzero(&gate, sizeof(gate)); def.sin6_len = mask.sin6_len = gate.sin6_len = sizeof(struct sockaddr_in6); def.sin6_family = mask.sin6_family = gate.sin6_family = AF_INET6; gate.sin6_addr = new->rtaddr; s = splnet(); (void)rtrequest(RTM_ADD, (struct sockaddr *)&def, (struct sockaddr *)&gate, (struct sockaddr *)&mask, - RTF_GATEWAY, NULL); + RTF_GATEWAY, &newrt); + if (newrt) { +#ifdef ND6_USE_RTSOCK + defrouter_msg(RTM_ADD, newrt); /* tell user process */ +#endif + newrt->rt_refcnt--; + } splx(s); return; } +/* Add a route to a given interface as default */ +void +defrouter_addifreq(ifp) + struct ifnet *ifp; +{ + struct sockaddr_in6 def, mask; + struct ifaddr *ifa; + struct rtentry *newrt = NULL; + int error, flags; + + bzero(&def, sizeof(def)); + bzero(&mask, sizeof(mask)); + + def.sin6_len = mask.sin6_len = sizeof(struct sockaddr_in6); + def.sin6_family = mask.sin6_family = AF_INET6; + + /* + * Search for an ifaddr beloging to the specified interface. + * XXX: An IPv6 address are required to be assigned on the interface. + */ + if ((ifa = ifaof_ifpforaddr((struct sockaddr *)&def, ifp)) == NULL) { + log(LOG_ERR, /* better error? */ + "defrouter_addifreq: failed to find an ifaddr " + "to install a route to interface %s\n", + if_name(ifp)); + return; + } + + flags = ifa->ifa_flags; + if ((ifp->if_flags & IFF_POINTOPOINT) != 0) + flags &= ~RTF_CLONING; + if ((error = rtrequest(RTM_ADD, (struct sockaddr *)&def, + ifa->ifa_addr, (struct sockaddr *)&mask, + flags, &newrt)) != 0) { + log(LOG_ERR, + "defrouter_addifreq: failed to install a route to " + "interface %s (errno = %d)\n", + if_name(ifp), error); + + if (newrt) /* maybe unnecessary, but do it for safety */ + newrt->rt_refcnt--; + } else { + if (newrt) { +#ifdef ND6_USE_RTSOCK + defrouter_msg(RTM_ADD, newrt); +#endif + newrt->rt_refcnt--; + } + } +} + struct nd_defrouter * defrouter_lookup(addr, ifp) struct in6_addr *addr; struct ifnet *ifp; { struct nd_defrouter *dr; - LIST_FOREACH(dr, &nd_defrouter, dr_entry) + for (dr = TAILQ_FIRST(&nd_defrouter); dr; + dr = TAILQ_NEXT(dr, dr_entry)) { if (dr->ifp == ifp && IN6_ARE_ADDR_EQUAL(addr, &dr->rtaddr)) return(dr); + } return(NULL); /* search failed */ } void defrouter_delreq(dr, dofree) struct nd_defrouter *dr; int dofree; { struct sockaddr_in6 def, mask, gate; + struct rtentry *oldrt = NULL; Bzero(&def, sizeof(def)); Bzero(&mask, sizeof(mask)); Bzero(&gate, sizeof(gate)); def.sin6_len = mask.sin6_len = gate.sin6_len = sizeof(struct sockaddr_in6); def.sin6_family = mask.sin6_family = gate.sin6_family = AF_INET6; gate.sin6_addr = dr->rtaddr; rtrequest(RTM_DELETE, (struct sockaddr *)&def, (struct sockaddr *)&gate, (struct sockaddr *)&mask, - RTF_GATEWAY, (struct rtentry **)0); + RTF_GATEWAY, &oldrt); + if (oldrt) { +#ifdef ND6_USE_RTSOCK + defrouter_msg(RTM_DELETE, oldrt); +#endif + if (oldrt->rt_refcnt <= 0) + oldrt->rt_refcnt++; /* XXX */ + rtfree(oldrt); + } - if (dofree) + if (dofree) /* XXX: necessary? */ free(dr, M_IP6NDP); - - if (!LIST_EMPTY(&nd_defrouter)) - defrouter_addreq(LIST_FIRST(&nd_defrouter)); - - /* - * xxx update the Destination Cache entries for all - * destinations using that neighbor as a router (7.2.5) - */ } void defrtrlist_del(dr) struct nd_defrouter *dr; { struct nd_defrouter *deldr = NULL; struct nd_prefix *pr; /* * Flush all the routing table entries that use the router * as a next hop. */ if (!ip6_forwarding && ip6_accept_rtadv) { /* above is a good condition? */ rt6_flush(&dr->rtaddr, dr->ifp); } - if (dr == LIST_FIRST(&nd_defrouter)) + if (dr == TAILQ_FIRST(&nd_defrouter)) deldr = dr; /* The router is primary. */ - LIST_REMOVE(dr, dr_entry); + TAILQ_REMOVE(&nd_defrouter, dr, dr_entry); /* * Also delete all the pointers to the router in each prefix lists. */ - LIST_FOREACH(pr, &nd_prefix, ndpr_entry) { + for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) { struct nd_pfxrouter *pfxrtr; if ((pfxrtr = pfxrtr_lookup(pr, dr)) != NULL) pfxrtr_del(pfxrtr); } pfxlist_onlink_check(); /* - * If the router is the primary one, delete the default route - * entry in the routing table. + * If the router is the primary one, choose a new one. + * Note that defrouter_select() will remove the current gateway + * from the routing table. */ if (deldr) - defrouter_delreq(deldr, 0); + defrouter_select(); + free(dr, M_IP6NDP); } +/* + * Default Router Selection according to Section 6.3.6 of RFC 2461: + * 1) Routers that are reachable or probably reachable should be + * preferred. + * 2) When no routers on the list are known to be reachable or + * probably reachable, routers SHOULD be selected in a round-robin + * fashion. + * 3) If the Default Router List is empty, assume that all + * destinations are on-link. + */ +void +defrouter_select() +{ + int s = splnet(); + struct nd_defrouter *dr, anydr; + struct rtentry *rt = NULL; + struct llinfo_nd6 *ln = NULL; + + /* + * Search for a (probably) reachable router from the list. + */ + for (dr = TAILQ_FIRST(&nd_defrouter); dr; + dr = TAILQ_NEXT(dr, dr_entry)) { + if ((rt = nd6_lookup(&dr->rtaddr, 0, dr->ifp)) && + (ln = (struct llinfo_nd6 *)rt->rt_llinfo) && + ND6_IS_LLINFO_PROBREACH(ln)) { + /* Got it, and move it to the head */ + TAILQ_REMOVE(&nd_defrouter, dr, dr_entry); + TAILQ_INSERT_HEAD(&nd_defrouter, dr, dr_entry); + break; + } + } + + if ((dr = TAILQ_FIRST(&nd_defrouter))) { + /* + * De-install the previous default gateway and install + * a new one. + * Note that if there is no reachable router in the list, + * the head entry will be used anyway. + * XXX: do we have to check the current routing table entry? + */ + bzero(&anydr, sizeof(anydr)); + defrouter_delreq(&anydr, 0); + defrouter_addreq(dr); + } + else { + /* + * The Default Router List is empty, so install the default + * route to an inteface. + * XXX: The specification does not say this mechanism should + * be restricted to hosts, but this would be not useful + * (even harmful) for routers. + */ + if (!ip6_forwarding) { + /* + * De-install the current default route + * in advance. + */ + bzero(&anydr, sizeof(anydr)); + defrouter_delreq(&anydr, 0); + if (nd6_defifp) { + /* + * Install a route to the default interface + * as default route. + */ + defrouter_addifreq(nd6_defifp); + } +#ifdef ND6_DEBUG + else /* noisy log? */ + log(LOG_INFO, "defrouter_select: " + "there's no default router and no default" + " interface\n"); +#endif + } + } + + splx(s); + return; +} + static struct nd_defrouter * defrtrlist_update(new) struct nd_defrouter *new; { struct nd_defrouter *dr, *n; int s = splnet(); if ((dr = defrouter_lookup(&new->rtaddr, new->ifp)) != NULL) { /* entry exists */ if (new->rtlifetime == 0) { defrtrlist_del(dr); dr = NULL; } else { /* override */ dr->flags = new->flags; /* xxx flag check */ dr->rtlifetime = new->rtlifetime; dr->expire = new->expire; } splx(s); return(dr); } /* entry does not exist */ if (new->rtlifetime == 0) { splx(s); return(NULL); } n = (struct nd_defrouter *)malloc(sizeof(*n), M_IP6NDP, M_NOWAIT); if (n == NULL) { splx(s); return(NULL); } bzero(n, sizeof(*n)); *n = *new; - if (LIST_EMPTY(&nd_defrouter)) { - LIST_INSERT_HEAD(&nd_defrouter, n, dr_entry); - defrouter_addreq(n); - } else { - LIST_INSERT_AFTER(LIST_FIRST(&nd_defrouter), n, dr_entry); - defrouter_addreq(n); - } + + /* + * Insert the new router at the end of the Default Router List. + * If there is no other router, install it anyway. Otherwise, + * just continue to use the current default router. + */ + TAILQ_INSERT_TAIL(&nd_defrouter, n, dr_entry); + if (TAILQ_FIRST(&nd_defrouter) == n) + defrouter_select(); splx(s); return(n); } static struct nd_pfxrouter * pfxrtr_lookup(pr, dr) struct nd_prefix *pr; struct nd_defrouter *dr; { struct nd_pfxrouter *search; - - LIST_FOREACH(search, &pr->ndpr_advrtrs, pfr_entry) { + + for (search = pr->ndpr_advrtrs.lh_first; search; search = search->pfr_next) { if (search->router == dr) break; } return(search); } static void pfxrtr_add(pr, dr) struct nd_prefix *pr; struct nd_defrouter *dr; { struct nd_pfxrouter *new; new = (struct nd_pfxrouter *)malloc(sizeof(*new), M_IP6NDP, M_NOWAIT); if (new == NULL) return; bzero(new, sizeof(*new)); new->router = dr; LIST_INSERT_HEAD(&pr->ndpr_advrtrs, new, pfr_entry); pfxlist_onlink_check(); } static void pfxrtr_del(pfr) struct nd_pfxrouter *pfr; { LIST_REMOVE(pfr, pfr_entry); free(pfr, M_IP6NDP); } static struct nd_prefix * prefix_lookup(pr) struct nd_prefix *pr; { struct nd_prefix *search; - LIST_FOREACH(search, &nd_prefix, ndpr_entry) { + for (search = nd_prefix.lh_first; search; search = search->ndpr_next) { if (pr->ndpr_ifp == search->ndpr_ifp && pr->ndpr_plen == search->ndpr_plen && in6_are_prefix_equal(&pr->ndpr_prefix.sin6_addr, &search->ndpr_prefix.sin6_addr, pr->ndpr_plen) ) { break; } } return(search); } static int prelist_add(pr, dr) struct nd_prefix *pr; struct nd_defrouter *dr; { struct nd_prefix *new; int i, s; new = (struct nd_prefix *)malloc(sizeof(*new), M_IP6NDP, M_NOWAIT); if (new == NULL) return ENOMEM; bzero(new, sizeof(*new)); *new = *pr; /* initilization */ new->ndpr_statef_onlink = pr->ndpr_statef_onlink; LIST_INIT(&new->ndpr_advrtrs); in6_prefixlen2mask(&new->ndpr_mask, new->ndpr_plen); /* make prefix in the canonical form */ for (i = 0; i < 4; i++) new->ndpr_prefix.sin6_addr.s6_addr32[i] &= new->ndpr_mask.s6_addr32[i]; /* xxx ND_OPT_PI_FLAG_ONLINK processing */ s = splnet(); /* link ndpr_entry to nd_prefix list */ LIST_INSERT_HEAD(&nd_prefix, new, ndpr_entry); splx(s); - if (dr) + if (dr) { pfxrtr_add(new, dr); + } return 0; } void prelist_remove(pr) struct nd_prefix *pr; { struct nd_pfxrouter *pfr, *next; int s; s = splnet(); /* unlink ndpr_entry from nd_prefix list */ LIST_REMOVE(pr, ndpr_entry); splx(s); /* free list of routers that adversed the prefix */ - for (pfr = LIST_FIRST(&pr->ndpr_advrtrs); pfr; pfr = next) { - next = LIST_NEXT(pfr, pfr_entry); + for (pfr = pr->ndpr_advrtrs.lh_first; pfr; pfr = next) { + next = pfr->pfr_next; free(pfr, M_IP6NDP); } free(pr, M_IP6NDP); pfxlist_onlink_check(); } /* * NOTE: We set address lifetime to keep * address lifetime <= prefix lifetime * invariant. This is to simplify on-link determination code. * If onlink determination is udated, this routine may have to be updated too. */ int prelist_update(new, dr, m) struct nd_prefix *new; struct nd_defrouter *dr; /* may be NULL */ struct mbuf *m; { struct in6_ifaddr *ia6 = NULL; struct nd_prefix *pr; int s = splnet(); int error = 0; int auth; struct in6_addrlifetime *lt6; + u_char onlink; /* Mobile IPv6 */ auth = 0; if (m) { /* * Authenticity for NA consists authentication for * both IP header and IP datagrams, doesn't it ? */ #if defined(M_AUTHIPHDR) && defined(M_AUTHIPDGM) auth = (m->m_flags & M_AUTHIPHDR && m->m_flags & M_AUTHIPDGM) ? 1 : 0; #endif } if ((pr = prefix_lookup(new)) != NULL) { if (pr->ndpr_ifp != new->ndpr_ifp) { error = EADDRNOTAVAIL; goto end; } + /* update prefix information */ pr->ndpr_flags = new->ndpr_flags; pr->ndpr_vltime = new->ndpr_vltime; pr->ndpr_pltime = new->ndpr_pltime; pr->ndpr_preferred = new->ndpr_preferred; pr->ndpr_expire = new->ndpr_expire; /* * RFC 2462 5.5.3 (d) or (e) * We got a prefix which we have seen in the past. */ if (!new->ndpr_raf_auto) goto noautoconf1; if (IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr)) ia6 = NULL; else ia6 = in6ifa_ifpwithaddr(pr->ndpr_ifp, &pr->ndpr_addr); if (ia6 == NULL) { /* * Special case: * (1) We have seen the prefix advertised before, but * we have never performed autoconfig for this prefix. * This is because Autonomous bit was 0 previously, or * autoconfig failed due to some other reasons. * (2) We have seen the prefix advertised before and * we have performed autoconfig in the past, but * we seem to have no interface address right now. * This is because the interface address have expired. * * This prefix is fresh, with respect to autoconfig * process. * * Add an address based on RFC 2462 5.5.3 (d). */ ia6 = in6_ifadd(pr->ndpr_ifp, &pr->ndpr_prefix.sin6_addr, &pr->ndpr_addr, new->ndpr_plen); if (!ia6) { error = EADDRNOTAVAIL; log(LOG_ERR, "prelist_update: failed to add a " "new address\n"); goto noautoconf1; } lt6 = &ia6->ia6_lifetime; /* address lifetime <= prefix lifetime */ lt6->ia6t_vltime = new->ndpr_vltime; lt6->ia6t_pltime = new->ndpr_pltime; - in6_init_address_ltimes(new, lt6); + in6_init_address_ltimes(new, lt6, 1); } else { -#define TWOHOUR (120*60) +#define TWOHOUR (120*60) /* * We have seen the prefix before, and we have added * interface address in the past. We still have * the interface address assigned. * * update address lifetime based on RFC 2462 * 5.5.3 (e). */ int update = 0; lt6 = &ia6->ia6_lifetime; - /* - * update to RFC 2462 5.5.3 (e) from Jim Bound, - * (ipng 6712) - */ +#if 0 /* RFC 2462 5.5.3 (e) */ + lt6->ia6t_pltime = new->ndpr_pltime; if (TWOHOUR < new->ndpr_vltime + || lt6pr->nd < new->ndpr_vltime) { + lt6->ia6t_vltime = new->ndpr_vltime; + update++; + } else if (auth + && lt6->ia6t_vltime <= TWOHOUR0 + && new->ndpr_vltime <= lt6->ia6t_vltime) { + lt6->ia6t_vltime = new->ndpr_vltime; + update++; + } else { + lt6->ia6t_vltime = TWOHOUR; + update++; + } + + /* 2 hour rule is not imposed for pref lifetime */ + new->ndpr_apltime = new->ndpr_pltime; + lt6->ia6t_pltime = new->ndpr_pltime; +#else /* update from Jim Bound, (ipng 6712) */ + if (TWOHOUR < new->ndpr_vltime || lt6->ia6t_vltime < new->ndpr_vltime) { lt6->ia6t_vltime = new->ndpr_vltime; update++; } else if (auth) { lt6->ia6t_vltime = new->ndpr_vltime; update++; } /* jim bound rule is not imposed for pref lifetime */ lt6->ia6t_pltime = new->ndpr_pltime; - - update++; - if (update) - in6_init_address_ltimes(new, lt6); +#endif + in6_init_address_ltimes(new, lt6, update); } noautoconf1: +#if 0 + /* address lifetime expire processing, RFC 2462 5.5.4. */ + if (pr->ndpr_preferred && pr->ndpr_preferred < time_second) { + struct in6_ifaddr *ia6; + + ia6 = in6ifa_ifpwithaddr(pr->ndpr_ifp, &pr->ndpr_addr); + if (ia6) + ia6->ia6_flags &= ~IN6_IFF_DEPRECATED; + } +#endif + + onlink = pr->ndpr_statef_onlink; /* Mobile IPv6 */ + if (dr && pfxrtr_lookup(pr, dr) == NULL) pfxrtr_add(pr, dr); + } else { int error_tmp; if (new->ndpr_vltime == 0) goto end; bzero(&new->ndpr_addr, sizeof(struct in6_addr)); /* * RFC 2462 5.5.3 (d) * We got a fresh prefix. Perform some sanity checks * and add an interface address by appending interface ID * to the advertised prefix. */ if (!new->ndpr_raf_auto) goto noautoconf2; ia6 = in6_ifadd(new->ndpr_ifp, &new->ndpr_prefix.sin6_addr, &new->ndpr_addr, new->ndpr_plen); if (!ia6) { error = EADDRNOTAVAIL; log(LOG_ERR, "prelist_update: " "failed to add a new address\n"); goto noautoconf2; } /* set onlink bit if an interface route is configured */ new->ndpr_statef_onlink = (ia6->ia_flags & IFA_ROUTE) ? 1 : 0; lt6 = &ia6->ia6_lifetime; /* address lifetime <= prefix lifetime */ lt6->ia6t_vltime = new->ndpr_vltime; lt6->ia6t_pltime = new->ndpr_pltime; - in6_init_address_ltimes(new, lt6); + in6_init_address_ltimes(new, lt6, 1); noautoconf2: error_tmp = prelist_add(new, dr); error = error_tmp ? error_tmp : error; } end: splx(s); return error; } /* + * A supplement function used in the on-link detection below; + * detect if a given prefix has a (probably) reachable advertising router. + * XXX: lengthy function name... + */ +struct nd_pfxrouter * +find_pfxlist_reachable_router(pr) + struct nd_prefix *pr; +{ + struct nd_pfxrouter *pfxrtr; + struct rtentry *rt; + struct llinfo_nd6 *ln; + + for (pfxrtr = LIST_FIRST(&pr->ndpr_advrtrs); pfxrtr; + pfxrtr = LIST_NEXT(pfxrtr, pfr_entry)) { + if ((rt = nd6_lookup(&pfxrtr->router->rtaddr, 0, + pfxrtr->router->ifp)) && + (ln = (struct llinfo_nd6 *)rt->rt_llinfo) && + ND6_IS_LLINFO_PROBREACH(ln)) + break; /* found */ + } + + return(pfxrtr); + +} + +/* * Check if each prefix in the prefix list has at least one available router - * that advertised the prefix. - * If the check fails, the prefix may be off-link because, for example, + * that advertised the prefix (A router is "available" if its neighbor cache + * entry has reachable or probably reachable). + * If the check fails, the prefix may be off-link, because, for example, * we have moved from the network but the lifetime of the prefix has not * been expired yet. So we should not use the prefix if there is another * prefix that has an available router. - * But if there is no prefix that has an availble router, we still regards + * But if there is no prefix that has an available router, we still regards * all the prefixes as on-link. This is because we can't tell if all the * routers are simply dead or if we really moved from the network and there * is no router around us. */ -static void +void pfxlist_onlink_check() { struct nd_prefix *pr; - LIST_FOREACH(pr, &nd_prefix, ndpr_entry) - if (!LIST_EMPTY(&pr->ndpr_advrtrs)) /* pr has an available router */ + /* + * Check if there is a prefix that has a reachable advertising + * router. + */ + for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) { + if (find_pfxlist_reachable_router(pr)) break; + } if (pr) { /* - * There is at least one prefix that has a router. First, - * detach prefixes which has no advertising router and then - * attach other prefixes. The order is important since an - * attached prefix and a detached prefix may have a same - * interface route. + * There is at least one prefix that has a reachable router. + * First, detach prefixes which has no reachable advertising + * router and then attach other prefixes. + * The order is important since an attached prefix and a + * detached prefix may have a same interface route. */ - LIST_FOREACH(pr, &nd_prefix, ndpr_entry) { - if (LIST_EMPTY(&pr->ndpr_advrtrs) && + for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) { + if (find_pfxlist_reachable_router(pr) == NULL && pr->ndpr_statef_onlink) { pr->ndpr_statef_onlink = 0; nd6_detach_prefix(pr); } } - LIST_FOREACH(pr, &nd_prefix, ndpr_entry) { - if (!LIST_EMPTY(&pr->ndpr_advrtrs) && - pr->ndpr_statef_onlink == 0) + for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) { + if (find_pfxlist_reachable_router(pr) && + pr->ndpr_statef_onlink == 0) nd6_attach_prefix(pr); } - } else { - /* there is no prefix that has a router */ - LIST_FOREACH(pr, &nd_prefix, ndpr_entry) + } + else { + /* there is no prefix that has a reachable router */ + for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) if (pr->ndpr_statef_onlink == 0) nd6_attach_prefix(pr); } } static void nd6_detach_prefix(pr) struct nd_prefix *pr; { struct in6_ifaddr *ia6; struct sockaddr_in6 sa6, mask6; /* * Delete the interface route associated with the prefix. */ bzero(&sa6, sizeof(sa6)); sa6.sin6_family = AF_INET6; sa6.sin6_len = sizeof(sa6); bcopy(&pr->ndpr_prefix.sin6_addr, &sa6.sin6_addr, sizeof(struct in6_addr)); bzero(&mask6, sizeof(mask6)); mask6.sin6_family = AF_INET6; mask6.sin6_len = sizeof(sa6); bcopy(&pr->ndpr_mask, &mask6.sin6_addr, sizeof(struct in6_addr)); { int e; e = rtrequest(RTM_DELETE, (struct sockaddr *)&sa6, NULL, (struct sockaddr *)&mask6, 0, NULL); if (e) { log(LOG_ERR, "nd6_detach_prefix: failed to delete route: " "%s/%d (errno = %d)\n", ip6_sprintf(&sa6.sin6_addr), pr->ndpr_plen, e); } } /* * Mark the address derived from the prefix detached so that * it won't be used as a source address for a new connection. */ if (IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr)) ia6 = NULL; else ia6 = in6ifa_ifpwithaddr(pr->ndpr_ifp, &pr->ndpr_addr); if (ia6) ia6->ia6_flags |= IN6_IFF_DETACHED; } static void nd6_attach_prefix(pr) struct nd_prefix *pr; { struct ifaddr *ifa; struct in6_ifaddr *ia6; /* * Add the interface route associated with the prefix(if necessary) * Should we consider if the L bit is set in pr->ndpr_flags? */ ifa = ifaof_ifpforaddr((struct sockaddr *)&pr->ndpr_prefix, pr->ndpr_ifp); if (ifa == NULL) { log(LOG_ERR, "nd6_attach_prefix: failed to find any ifaddr" " to add route for a prefix(%s/%d)\n", ip6_sprintf(&pr->ndpr_addr), pr->ndpr_plen); - } else { + } + else { int e; struct sockaddr_in6 mask6; bzero(&mask6, sizeof(mask6)); mask6.sin6_family = AF_INET6; mask6.sin6_len = sizeof(mask6); mask6.sin6_addr = pr->ndpr_mask; e = rtrequest(RTM_ADD, (struct sockaddr *)&pr->ndpr_prefix, ifa->ifa_addr, (struct sockaddr *)&mask6, ifa->ifa_flags, NULL); if (e == 0) pr->ndpr_statef_onlink = 1; else { log(LOG_ERR, "nd6_attach_prefix: failed to add route for" " a prefix(%s/%d), errno = %d\n", ip6_sprintf(&pr->ndpr_addr), pr->ndpr_plen, e); } } /* * Now the address derived from the prefix can be used as a source * for a new connection, so clear the detached flag. */ if (IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr)) ia6 = NULL; else ia6 = in6ifa_ifpwithaddr(pr->ndpr_ifp, &pr->ndpr_addr); if (ia6) { ia6->ia6_flags &= ~IN6_IFF_DETACHED; if (pr->ndpr_statef_onlink) ia6->ia_flags |= IFA_ROUTE; } } static struct in6_ifaddr * in6_ifadd(ifp, in6, addr, prefixlen) struct ifnet *ifp; struct in6_addr *in6; struct in6_addr *addr; int prefixlen; /* prefix len of the new prefix in "in6" */ { struct ifaddr *ifa; struct in6_ifaddr *ia, *ib, *oia; int s, error; struct in6_addr mask; in6_len2mask(&mask, prefixlen); /* find link-local address (will be interface ID) */ - ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp); + ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp, 0);/* 0 is OK? */ if (ifa) ib = (struct in6_ifaddr *)ifa; else return NULL; +#if 0 /* don't care link local addr state, and always do DAD */ + /* if link-local address is not eligible, do not autoconfigure. */ + if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_NOTREADY) { + printf("in6_ifadd: link-local address not ready\n"); + return NULL; + } +#endif + /* prefixlen + ifidlen must be equal to 128 */ if (prefixlen != in6_mask2len(&ib->ia_prefixmask.sin6_addr)) { log(LOG_ERR, "in6_ifadd: wrong prefixlen for %s" "(prefix=%d ifid=%d)\n", if_name(ifp), prefixlen, 128 - in6_mask2len(&ib->ia_prefixmask.sin6_addr)); return NULL; } /* make ifaddr */ ia = (struct in6_ifaddr *)malloc(sizeof(*ia), M_IFADDR, M_DONTWAIT); if (ia == NULL) { printf("ENOBUFS in in6_ifadd %d\n", __LINE__); return NULL; } bzero((caddr_t)ia, sizeof(*ia)); ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr; - ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr; + if (ifp->if_flags & IFF_POINTOPOINT) + ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr; + else + ia->ia_ifa.ifa_dstaddr = NULL; ia->ia_ifa.ifa_netmask = (struct sockaddr *)&ia->ia_prefixmask; ia->ia_ifp = ifp; /* link to in6_ifaddr */ if ((oia = in6_ifaddr) != NULL) { for( ; oia->ia_next; oia = oia->ia_next) continue; oia->ia_next = ia; - } else + } else { + /* + * This should be impossible, since we have at least one + * link-local address (see the beginning of this function). + * XXX: should we rather panic here? + */ + printf("in6_ifadd: in6_ifaddr is NULL (impossible!)\n"); in6_ifaddr = ia; + } + /* gain a refcnt for the link from in6_ifaddr */ + ia->ia_ifa.ifa_refcnt++; /* link to if_addrlist */ - if (!TAILQ_EMPTY(&ifp->if_addrlist)) { - TAILQ_INSERT_TAIL(&ifp->if_addrlist, (struct ifaddr *)ia, - ifa_list); - } + TAILQ_INSERT_TAIL(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list); + /* gain another refcnt for the link from if_addrlist */ + ia->ia_ifa.ifa_refcnt++; /* new address */ ia->ia_addr.sin6_len = sizeof(struct sockaddr_in6); ia->ia_addr.sin6_family = AF_INET6; /* prefix */ bcopy(in6, &ia->ia_addr.sin6_addr, sizeof(ia->ia_addr.sin6_addr)); ia->ia_addr.sin6_addr.s6_addr32[0] &= mask.s6_addr32[0]; ia->ia_addr.sin6_addr.s6_addr32[1] &= mask.s6_addr32[1]; ia->ia_addr.sin6_addr.s6_addr32[2] &= mask.s6_addr32[2]; ia->ia_addr.sin6_addr.s6_addr32[3] &= mask.s6_addr32[3]; /* interface ID */ ia->ia_addr.sin6_addr.s6_addr32[0] |= (ib->ia_addr.sin6_addr.s6_addr32[0] & ~mask.s6_addr32[0]); ia->ia_addr.sin6_addr.s6_addr32[1] |= (ib->ia_addr.sin6_addr.s6_addr32[1] & ~mask.s6_addr32[1]); ia->ia_addr.sin6_addr.s6_addr32[2] |= (ib->ia_addr.sin6_addr.s6_addr32[2] & ~mask.s6_addr32[2]); ia->ia_addr.sin6_addr.s6_addr32[3] |= (ib->ia_addr.sin6_addr.s6_addr32[3] & ~mask.s6_addr32[3]); /* new prefix */ ia->ia_prefixmask.sin6_len = sizeof(struct sockaddr_in6); ia->ia_prefixmask.sin6_family = AF_INET6; bcopy(&mask, &ia->ia_prefixmask.sin6_addr, sizeof(ia->ia_prefixmask.sin6_addr)); /* same routine */ ia->ia_ifa.ifa_rtrequest = (ifp->if_type == IFT_PPP) ? nd6_p2p_rtrequest : nd6_rtrequest; ia->ia_ifa.ifa_flags |= RTF_CLONING; ia->ia_ifa.ifa_metric = ifp->if_metric; /* add interface route */ if ((error = rtinit(&(ia->ia_ifa), (int)RTM_ADD, RTF_UP|RTF_CLONING))) { log(LOG_NOTICE, "in6_ifadd: failed to add an interface route " "for %s/%d on %s, errno = %d\n", ip6_sprintf(&ia->ia_addr.sin6_addr), prefixlen, if_name(ifp), error); } else ia->ia_flags |= IFA_ROUTE; *addr = ia->ia_addr.sin6_addr; if (ifp->if_flags & IFF_MULTICAST) { int error; /* not used */ struct in6_addr sol6; /* join solicited node multicast address */ bzero(&sol6, sizeof(sol6)); sol6.s6_addr16[0] = htons(0xff02); sol6.s6_addr16[1] = htons(ifp->if_index); sol6.s6_addr32[1] = 0; sol6.s6_addr32[2] = htonl(1); sol6.s6_addr32[3] = ia->ia_addr.sin6_addr.s6_addr32[3]; sol6.s6_addr8[12] = 0xff; (void)in6_addmulti(&sol6, ifp, &error); } ia->ia6_flags |= IN6_IFF_TENTATIVE; /* * To make the interface up. Only AF_INET6 in ia is used... */ s = splimp(); if (ifp->if_ioctl && (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia)) { splx(s); return NULL; } splx(s); /* Perform DAD, if needed. */ nd6_dad_start((struct ifaddr *)ia, NULL); return ia; } int in6_ifdel(ifp, in6) struct ifnet *ifp; struct in6_addr *in6; { struct in6_ifaddr *ia = (struct in6_ifaddr *)NULL; struct in6_ifaddr *oia = (struct in6_ifaddr *)NULL; if (!ifp) return -1; ia = in6ifa_ifpwithaddr(ifp, in6); if (!ia) return -1; if (ifp->if_flags & IFF_MULTICAST) { /* * delete solicited multicast addr for deleting host id */ struct in6_multi *in6m; struct in6_addr llsol; bzero(&llsol, sizeof(struct in6_addr)); llsol.s6_addr16[0] = htons(0xff02); llsol.s6_addr16[1] = htons(ifp->if_index); llsol.s6_addr32[1] = 0; llsol.s6_addr32[2] = htonl(1); llsol.s6_addr32[3] = ia->ia_addr.sin6_addr.s6_addr32[3]; llsol.s6_addr8[12] = 0xff; IN6_LOOKUP_MULTI(llsol, ifp, in6m); if (in6m) in6_delmulti(in6m); } if (ia->ia_flags & IFA_ROUTE) { rtinit(&(ia->ia_ifa), (int)RTM_DELETE, 0); ia->ia_flags &= ~IFA_ROUTE; } TAILQ_REMOVE(&ifp->if_addrlist, (struct ifaddr *)ia, ifa_list); + IFAFREE(&ia->ia_ifa); /* lladdr is never deleted */ oia = ia; if (oia == (ia = in6_ifaddr)) in6_ifaddr = ia->ia_next; else { while (ia->ia_next && (ia->ia_next != oia)) ia = ia->ia_next; if (ia->ia_next) ia->ia_next = oia->ia_next; else return -1; } IFAFREE((&oia->ia_ifa)); /* xxx rtrequest(RTM_DELETE, (struct sockaddr *)&ia->ia_addr, (struct sockaddr *)0 (struct sockaddr *)&ia->ia_prefixmask, RTF_UP|RTF_CLONING, (struct rtentry **)0); */ return 0; } int in6_init_prefix_ltimes(struct nd_prefix *ndpr) { /* check if preferred lifetime > valid lifetime */ if (ndpr->ndpr_pltime > ndpr->ndpr_vltime) { log(LOG_INFO, "in6_init_prefix_ltimes: preferred lifetime" "(%d) is greater than valid lifetime(%d)\n", (u_int)ndpr->ndpr_pltime, (u_int)ndpr->ndpr_vltime); return (EINVAL); } if (ndpr->ndpr_pltime == ND6_INFINITE_LIFETIME) ndpr->ndpr_preferred = 0; else ndpr->ndpr_preferred = time_second + ndpr->ndpr_pltime; if (ndpr->ndpr_vltime == ND6_INFINITE_LIFETIME) ndpr->ndpr_expire = 0; else ndpr->ndpr_expire = time_second + ndpr->ndpr_vltime; return 0; } static void -in6_init_address_ltimes(struct nd_prefix *new, struct in6_addrlifetime *lt6) +in6_init_address_ltimes(struct nd_prefix *new, + struct in6_addrlifetime *lt6, + int update_vltime) { - /* init ia6t_expire */ - if (lt6->ia6t_vltime == ND6_INFINITE_LIFETIME) - lt6->ia6t_expire = 0; - else { - lt6->ia6t_expire = time_second; - lt6->ia6t_expire += lt6->ia6t_vltime; + /* Valid lifetime must not be updated unless explicitly specified. */ + if (update_vltime) { + /* init ia6t_expire */ + if (lt6->ia6t_vltime == ND6_INFINITE_LIFETIME) + lt6->ia6t_expire = 0; + else { + lt6->ia6t_expire = time_second; + lt6->ia6t_expire += lt6->ia6t_vltime; + } + /* Ensure addr lifetime <= prefix lifetime. */ + if (new->ndpr_expire && lt6->ia6t_expire && + new->ndpr_expire < lt6->ia6t_expire) + lt6->ia6t_expire = new->ndpr_expire; } + /* init ia6t_preferred */ if (lt6->ia6t_pltime == ND6_INFINITE_LIFETIME) lt6->ia6t_preferred = 0; else { lt6->ia6t_preferred = time_second; lt6->ia6t_preferred += lt6->ia6t_pltime; } - /* ensure addr lifetime <= prefix lifetime */ - if (new->ndpr_expire && lt6->ia6t_expire && - new->ndpr_expire < lt6->ia6t_expire) - lt6->ia6t_expire = new->ndpr_expire; + /* Ensure addr lifetime <= prefix lifetime. */ if (new->ndpr_preferred && lt6->ia6t_preferred && new->ndpr_preferred < lt6->ia6t_preferred) lt6->ia6t_preferred = new->ndpr_preferred; } /* * Delete all the routing table entries that use the specified gateway. * XXX: this function causes search through all entries of routing table, so * it shouldn't be called when acting as a router. */ void rt6_flush(gateway, ifp) struct in6_addr *gateway; struct ifnet *ifp; { struct radix_node_head *rnh = rt_tables[AF_INET6]; int s = splnet(); /* We'll care only link-local addresses */ if (!IN6_IS_ADDR_LINKLOCAL(gateway)) { splx(s); return; } /* XXX: hack for KAME's link-local address kludge */ gateway->s6_addr16[1] = htons(ifp->if_index); rnh->rnh_walktree(rnh, rt6_deleteroute, (void *)gateway); splx(s); } static int rt6_deleteroute(rn, arg) struct radix_node *rn; void *arg; { #define SIN6(s) ((struct sockaddr_in6 *)s) struct rtentry *rt = (struct rtentry *)rn; struct in6_addr *gate = (struct in6_addr *)arg; if (rt->rt_gateway == NULL || rt->rt_gateway->sa_family != AF_INET6) return(0); if (!IN6_ARE_ADDR_EQUAL(gate, &SIN6(rt->rt_gateway)->sin6_addr)) return(0); /* * We delete only host route. This means, in particular, we don't * delete default route. */ if ((rt->rt_flags & RTF_HOST) == 0) return(0); return(rtrequest(RTM_DELETE, rt_key(rt), rt->rt_gateway, rt_mask(rt), rt->rt_flags, 0)); #undef SIN6 +} + +int +nd6_setdefaultiface(ifindex) + int ifindex; +{ + int error = 0; + + if (ifindex < 0 || if_index < ifindex) + return(EINVAL); + + if (nd6_defifindex != ifindex) { + nd6_defifindex = ifindex; + if (nd6_defifindex > 0) + nd6_defifp = ifindex2ifnet[nd6_defifindex]; + else + nd6_defifp = NULL; + + /* + * If the Default Router List is empty, install a route + * to the specified interface as default or remove the default + * route when the default interface becomes canceled. + * The check for the queue is actually redundant, but + * we do this here to avoid re-install the default route + * if the list is NOT empty. + */ + if (TAILQ_FIRST(&nd_defrouter) == NULL) + defrouter_select(); + + /* + * Our current implementation assumes one-to-one maping between + * interfaces and links, so it would be natural to use the + * default interface as the default link. + */ + scope6_setdefault(nd6_defifp); + } + + return(error); } Index: head/sys/netinet6/pim6.h =================================================================== --- head/sys/netinet6/pim6.h (revision 62586) +++ head/sys/netinet6/pim6.h (revision 62587) @@ -1,68 +1,69 @@ +/* $FreeBSD$ */ +/* $KAME: pim6.h,v 1.3 2000/03/25 07:23:58 sumikawa Exp $ */ + /* * Copyright (C) 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* * Protocol Independent Multicast (PIM) definitions * * Written by Ahmed Helmy, SGI, July 1996 * * MULTICAST */ /* * PIM packet header */ -#define PIM_VERSION 2 +#define PIM_VERSION 2 struct pim { #if defined(BYTE_ORDER) && (BYTE_ORDER == LITTLE_ENDIAN) u_char pim_type:4, /* the PIM message type, currently they are: * Hello, Register, Register-Stop, Join/Prune, * Bootstrap, Assert, Graft (PIM-DM only), * Graft-Ack (PIM-DM only), C-RP-Adv */ pim_ver:4; /* PIM version number; 2 for PIMv2 */ #else u_char pim_ver:4, /* PIM version */ pim_type:4; /* PIM type */ #endif - u_char pim_rsv; /* Reserved */ + u_char pim_rsv; /* Reserved */ u_short pim_cksum; /* IP style check sum */ }; -#define PIM_MINLEN 8 /* The header min. length is 8 */ -#define PIM6_REG_MINLEN (PIM_MINLEN+40) /* Register message + inner IP6 header */ +#define PIM_MINLEN 8 /* The header min. length is 8 */ +#define PIM6_REG_MINLEN (PIM_MINLEN+40) /* Register message + inner IP6 header */ /* * Message types */ -#define PIM_REGISTER 1 /* PIM Register type is 1 */ +#define PIM_REGISTER 1 /* PIM Register type is 1 */ /* second bit in reg_head is the null bit */ -#define PIM_NULL_REGISTER 0x40000000 +#define PIM_NULL_REGISTER 0x40000000 Index: head/sys/netinet6/pim6_var.h =================================================================== --- head/sys/netinet6/pim6_var.h (revision 62586) +++ head/sys/netinet6/pim6_var.h (revision 62587) @@ -1,71 +1,70 @@ +/* $FreeBSD$ */ +/* $KAME: pim6_var.h,v 1.8 2000/06/06 08:07:43 jinmei Exp $ */ + /* * Copyright (C) 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ -/* $Id: pim6_var.h,v 1.2 1999/08/01 15:58:13 itojun Exp $ */ #ifndef _NETINET6_PIM6_VAR_H_ #define _NETINET6_PIM6_VAR_H_ /* * Protocol Independent Multicast (PIM), * implementation-specific definitions. * * Written by George Edmond Eddy (Rusty), ISI, February 1998 * Modified by Pavlin Ivanov Radoslavov, USC/ISI, May 1998 */ struct pim6stat { - u_int pim6s_rcv_total; /* total PIM messages received */ - u_int pim6s_rcv_tooshort; /* received with too few bytes */ - u_int pim6s_rcv_badsum; /* received with bad checksum */ - u_int pim6s_rcv_badversion; /* received bad PIM version */ - u_int pim6s_rcv_registers; /* received registers */ - u_int pim6s_rcv_badregisters; /* received invalid registers */ - u_int pim6s_snd_registers; /* sent registers */ + u_quad_t pim6s_rcv_total; /* total PIM messages received */ + u_quad_t pim6s_rcv_tooshort; /* received with too few bytes */ + u_quad_t pim6s_rcv_badsum; /* received with bad checksum */ + u_quad_t pim6s_rcv_badversion; /* received bad PIM version */ + u_quad_t pim6s_rcv_registers; /* received registers */ + u_quad_t pim6s_rcv_badregisters; /* received invalid registers */ + u_quad_t pim6s_snd_registers; /* sent registers */ }; -#ifdef _KERNEL -extern struct pim6stat pim6stat; +#if (defined(KERNEL)) || (defined(_KERNEL)) +extern struct pim6stat pim6stat; -int pim6_input __P((struct mbuf **, int*, int)); -#endif +int pim6_input __P((struct mbuf **, int*, int)); +#endif /* KERNEL */ /* * Names for PIM sysctl objects */ -#define PIMCTL_STATS 1 /* statistics (read-only) */ -#define PIMCTL_MAXID 2 +#define PIM6CTL_STATS 1 /* statistics (read-only) */ +#define PIM6CTL_MAXID 2 -#define PIMCTL_NAMES { \ +#define PIM6CTL_NAMES { \ { 0, 0 }, \ { 0, 0 }, \ } - #endif /* _NETINET6_PIM6_VAR_H_ */ Index: head/sys/netinet6/raw_ip6.c =================================================================== --- head/sys/netinet6/raw_ip6.c (revision 62586) +++ head/sys/netinet6/raw_ip6.c (revision 62587) @@ -1,629 +1,715 @@ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * Copyright (c) 1982, 1986, 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)raw_ip.c 8.2 (Berkeley) 1/4/94 */ #include "opt_ipsec.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include -#include +#include #include #include #include +#include +#ifdef ENABLE_DEFAULT_SCOPE +#include +#endif #ifdef IPSEC #include #include #endif /*IPSEC*/ #include #include "faith.h" #define satosin6(sa) ((struct sockaddr_in6 *)(sa)) #define ifatoia6(ifa) ((struct in6_ifaddr *)(ifa)) /* * Raw interface to IP6 protocol. */ extern struct inpcbhead ripcb; extern struct inpcbinfo ripcbinfo; extern u_long rip_sendspace; extern u_long rip_recvspace; /* * Setup generic address and protocol structures * for raw_input routine, then pass them along with * mbuf chain. */ int rip6_input(mp, offp, proto) struct mbuf **mp; int *offp, proto; { struct mbuf *m = *mp; register struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); register struct inpcb *in6p; struct inpcb *last = 0; struct mbuf *opts = 0; struct sockaddr_in6 rip6src; #if defined(NFAITH) && 0 < NFAITH if (m->m_pkthdr.rcvif) { if (m->m_pkthdr.rcvif->if_type == IFT_FAITH) { /* XXX send icmp6 host/port unreach? */ m_freem(m); return IPPROTO_DONE; } } #endif init_sin6(&rip6src, m); /* general init */ LIST_FOREACH(in6p, &ripcb, inp_list) { if ((in6p->in6p_vflag & INP_IPV6) == 0) continue; if (in6p->in6p_ip6_nxt && in6p->in6p_ip6_nxt != proto) continue; if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr) && !IN6_ARE_ADDR_EQUAL(&in6p->in6p_laddr, &ip6->ip6_dst)) continue; if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr) && !IN6_ARE_ADDR_EQUAL(&in6p->in6p_faddr, &ip6->ip6_src)) continue; if (in6p->in6p_cksum != -1 && in6_cksum(m, ip6->ip6_nxt, *offp, m->m_pkthdr.len - *offp)) { /* XXX bark something */ continue; } if (last) { struct mbuf *n = m_copy(m, 0, (int)M_COPYALL); if (n) { if (last->in6p_flags & IN6P_CONTROLOPTS || last->in6p_socket->so_options & SO_TIMESTAMP) ip6_savecontrol(last, &opts, ip6, n); /* strip intermediate headers */ m_adj(n, *offp); if (sbappendaddr(&last->in6p_socket->so_rcv, (struct sockaddr *)&rip6src, n, opts) == 0) { /* should notify about lost packet */ m_freem(n); if (opts) m_freem(opts); } else sorwakeup(last->in6p_socket); opts = NULL; } } last = in6p; } if (last) { if (last->in6p_flags & IN6P_CONTROLOPTS || last->in6p_socket->so_options & SO_TIMESTAMP) ip6_savecontrol(last, &opts, ip6, m); /* strip intermediate headers */ m_adj(m, *offp); if (sbappendaddr(&last->in6p_socket->so_rcv, (struct sockaddr *)&rip6src, m, opts) == 0) { m_freem(m); if (opts) m_freem(opts); } else sorwakeup(last->in6p_socket); } else { if (proto == IPPROTO_NONE) m_freem(m); else { char *prvnxtp = ip6_get_prevhdr(m, *offp); /* XXX */ icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_NEXTHEADER, prvnxtp - mtod(m, char *)); } ip6stat.ip6s_delivered--; } return IPPROTO_DONE; } +void +rip6_ctlinput(cmd, sa, d) + int cmd; + struct sockaddr *sa; + void *d; +{ + struct sockaddr_in6 sa6; + struct ip6_hdr *ip6; + struct mbuf *m; + int off = 0; + void (*notify) __P((struct inpcb *, int)) = in6_rtchange; + + if (sa->sa_family != AF_INET6 || + sa->sa_len != sizeof(struct sockaddr_in6)) + return; + + if ((unsigned)cmd >= PRC_NCMDS) + return; + if (PRC_IS_REDIRECT(cmd)) + notify = in6_rtchange, d = NULL; + else if (cmd == PRC_HOSTDEAD) + d = NULL; + else if (inet6ctlerrmap[cmd] == 0) + return; + + /* if the parameter is from icmp6, decode it. */ + if (d != NULL) { + struct ip6ctlparam *ip6cp = (struct ip6ctlparam *)d; + m = ip6cp->ip6c_m; + ip6 = ip6cp->ip6c_ip6; + off = ip6cp->ip6c_off; + } else { + m = NULL; + ip6 = NULL; + } + + /* translate addresses into internal form */ + sa6 = *(struct sockaddr_in6 *)sa; + if (IN6_IS_ADDR_LINKLOCAL(&sa6.sin6_addr) && m && m->m_pkthdr.rcvif) + sa6.sin6_addr.s6_addr16[1] = htons(m->m_pkthdr.rcvif->if_index); + + if (ip6) { + /* + * XXX: We assume that when IPV6 is non NULL, + * M and OFF are valid. + */ + struct in6_addr s; + + /* translate addresses into internal form */ + memcpy(&s, &ip6->ip6_src, sizeof(s)); + if (IN6_IS_ADDR_LINKLOCAL(&s)) + s.s6_addr16[1] = htons(m->m_pkthdr.rcvif->if_index); + + (void) in6_pcbnotify(&ripcb, (struct sockaddr *)&sa6, + 0, &s, 0, cmd, notify); + } else + (void) in6_pcbnotify(&ripcb, (struct sockaddr *)&sa6, 0, + &zeroin6_addr, 0, cmd, notify); +} + /* * Generate IPv6 header and pass packet to ip6_output. * Tack on options user may have setup with control call. */ int #if __STDC__ rip6_output(struct mbuf *m, ...) #else rip6_output(m, va_alist) struct mbuf *m; va_dcl #endif { struct socket *so; struct sockaddr_in6 *dstsock; struct mbuf *control; struct in6_addr *dst; struct ip6_hdr *ip6; struct inpcb *in6p; u_int plen = m->m_pkthdr.len; int error = 0; struct ip6_pktopts opt, *optp = 0; struct ifnet *oifp = NULL; int type = 0, code = 0; /* for ICMPv6 output statistics only */ int priv = 0; va_list ap; va_start(ap, m); so = va_arg(ap, struct socket *); dstsock = va_arg(ap, struct sockaddr_in6 *); control = va_arg(ap, struct mbuf *); va_end(ap); in6p = sotoin6pcb(so); priv = 0; if (so->so_cred->cr_uid == 0) priv = 1; dst = &dstsock->sin6_addr; if (control) { if ((error = ip6_setpktoptions(control, &opt, priv)) != 0) goto bad; optp = &opt; } else optp = in6p->in6p_outputopts; /* * For an ICMPv6 packet, we should know its type and code * to update statistics. */ if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) { struct icmp6_hdr *icmp6; if (m->m_len < sizeof(struct icmp6_hdr) && (m = m_pullup(m, sizeof(struct icmp6_hdr))) == NULL) { error = ENOBUFS; goto bad; } icmp6 = mtod(m, struct icmp6_hdr *); type = icmp6->icmp6_type; code = icmp6->icmp6_code; } M_PREPEND(m, sizeof(*ip6), M_WAIT); ip6 = mtod(m, struct ip6_hdr *); /* * Next header might not be ICMP6 but use its pseudo header anyway. */ ip6->ip6_dst = *dst; /* * If the scope of the destination is link-local, embed the interface * index in the address. * * XXX advanced-api value overrides sin6_scope_id */ if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) { struct in6_pktinfo *pi; /* * XXX Boundary check is assumed to be already done in * ip6_setpktoptions(). */ if (optp && (pi = optp->ip6po_pktinfo) && pi->ipi6_ifindex) { ip6->ip6_dst.s6_addr16[1] = htons(pi->ipi6_ifindex); oifp = ifindex2ifnet[pi->ipi6_ifindex]; } else if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) && in6p->in6p_moptions && in6p->in6p_moptions->im6o_multicast_ifp) { oifp = in6p->in6p_moptions->im6o_multicast_ifp; ip6->ip6_dst.s6_addr16[1] = htons(oifp->if_index); } else if (dstsock->sin6_scope_id) { /* boundary check */ if (dstsock->sin6_scope_id < 0 || if_index < dstsock->sin6_scope_id) { error = ENXIO; /* XXX EINVAL? */ goto bad; } ip6->ip6_dst.s6_addr16[1] = htons(dstsock->sin6_scope_id & 0xffff);/*XXX*/ } } /* * Source address selection. */ { struct in6_addr *in6a; if ((in6a = in6_selectsrc(dstsock, optp, in6p->in6p_moptions, &in6p->in6p_route, &in6p->in6p_laddr, &error)) == 0) { if (error == 0) error = EADDRNOTAVAIL; goto bad; } ip6->ip6_src = *in6a; if (in6p->in6p_route.ro_rt) oifp = ifindex2ifnet[in6p->in6p_route.ro_rt->rt_ifp->if_index]; } ip6->ip6_flow = (ip6->ip6_flow & ~IPV6_FLOWINFO_MASK) | (in6p->in6p_flowinfo & IPV6_FLOWINFO_MASK); ip6->ip6_vfc = (ip6->ip6_vfc & ~IPV6_VERSION_MASK) | (IPV6_VERSION & IPV6_VERSION_MASK); /* ip6_plen will be filled in ip6_output, so not fill it here. */ ip6->ip6_nxt = in6p->in6p_ip6_nxt; ip6->ip6_hlim = in6_selecthlim(in6p, oifp); if (so->so_proto->pr_protocol == IPPROTO_ICMPV6 || in6p->in6p_cksum != -1) { struct mbuf *n; int off; u_int16_t *p; #define offsetof(type, member) ((size_t)(&((type *)0)->member)) /* XXX */ /* compute checksum */ if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) off = offsetof(struct icmp6_hdr, icmp6_cksum); else off = in6p->in6p_cksum; if (plen < off + 1) { error = EINVAL; goto bad; } off += sizeof(struct ip6_hdr); n = m; while (n && n->m_len <= off) { off -= n->m_len; n = n->m_next; } if (!n) goto bad; p = (u_int16_t *)(mtod(n, caddr_t) + off); *p = 0; *p = in6_cksum(m, ip6->ip6_nxt, sizeof(*ip6), plen); } #ifdef IPSEC - m->m_pkthdr.rcvif = (struct ifnet *)so; + ipsec_setsocket(m, so); #endif /*IPSEC*/ - error = ip6_output(m, optp, &in6p->in6p_route, IPV6_SOCKINMRCVIF, + error = ip6_output(m, optp, &in6p->in6p_route, 0, in6p->in6p_moptions, &oifp); if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) { if (oifp) icmp6_ifoutstat_inc(oifp, type, code); icmp6stat.icp6s_outhist[type]++; } goto freectl; bad: if (m) m_freem(m); freectl: if (optp == &opt && optp->ip6po_rthdr && optp->ip6po_route.ro_rt) RTFREE(optp->ip6po_route.ro_rt); if (control) m_freem(control); return(error); } /* * Raw IPv6 socket option processing. */ int rip6_ctloutput(so, sopt) struct socket *so; struct sockopt *sopt; { int error; if (sopt->sopt_level == IPPROTO_ICMPV6) /* * XXX: is it better to call icmp6_ctloutput() directly * from protosw? */ return(icmp6_ctloutput(so, sopt)); else if (sopt->sopt_level != IPPROTO_IPV6) return (EINVAL); error = 0; switch (sopt->sopt_dir) { case SOPT_GET: switch (sopt->sopt_name) { case MRT6_INIT: case MRT6_DONE: case MRT6_ADD_MIF: case MRT6_DEL_MIF: case MRT6_ADD_MFC: case MRT6_DEL_MFC: case MRT6_PIM: error = ip6_mrouter_get(so, sopt); break; default: error = ip6_ctloutput(so, sopt); break; } break; case SOPT_SET: switch (sopt->sopt_name) { case MRT6_INIT: case MRT6_DONE: case MRT6_ADD_MIF: case MRT6_DEL_MIF: case MRT6_ADD_MFC: case MRT6_DEL_MFC: case MRT6_PIM: error = ip6_mrouter_set(so, sopt); break; default: error = ip6_ctloutput(so, sopt); break; } break; } return (error); } static int rip6_attach(struct socket *so, int proto, struct proc *p) { struct inpcb *inp; int error, s; inp = sotoinpcb(so); if (inp) panic("rip6_attach"); if (p && (error = suser(p)) != 0) return error; error = soreserve(so, rip_sendspace, rip_recvspace); if (error) return error; s = splnet(); error = in_pcballoc(so, &ripcbinfo, p); splx(s); if (error) return error; inp = (struct inpcb *)so->so_pcb; inp->inp_vflag |= INP_IPV6; inp->in6p_ip6_nxt = (long)proto; inp->in6p_hops = -1; /* use kernel default */ inp->in6p_cksum = -1; #ifdef IPSEC error = ipsec_init_policy(so, &inp->in6p_sp); if (error != 0) { in6_pcbdetach(inp); return (error); } #endif /*IPSEC*/ MALLOC(inp->in6p_icmp6filt, struct icmp6_filter *, sizeof(struct icmp6_filter), M_PCB, M_NOWAIT); ICMP6_FILTER_SETPASSALL(inp->in6p_icmp6filt); return 0; } static int rip6_detach(struct socket *so) { struct inpcb *inp; inp = sotoinpcb(so); if (inp == 0) panic("rip6_detach"); /* xxx: RSVP */ if (so == ip6_mrouter) ip6_mrouter_done(); if (inp->in6p_icmp6filt) { FREE(inp->in6p_icmp6filt, M_PCB); inp->in6p_icmp6filt = NULL; } in6_pcbdetach(inp); return 0; } static int rip6_abort(struct socket *so) { soisdisconnected(so); return rip6_detach(so); } static int rip6_disconnect(struct socket *so) { struct inpcb *inp = sotoinpcb(so); if ((so->so_state & SS_ISCONNECTED) == 0) return ENOTCONN; inp->in6p_faddr = in6addr_any; return rip6_abort(so); } static int rip6_bind(struct socket *so, struct sockaddr *nam, struct proc *p) { struct inpcb *inp = sotoinpcb(so); struct sockaddr_in6 *addr = (struct sockaddr_in6 *)nam; struct ifaddr *ia = NULL; if (nam->sa_len != sizeof(*addr)) return EINVAL; if (TAILQ_EMPTY(&ifnet) || addr->sin6_family != AF_INET6) return EADDRNOTAVAIL; +#ifdef ENABLE_DEFAULT_SCOPE + if (addr->sin6_scope_id == 0) { /* not change if specified */ + addr->sin6_scope_id = scope6_addr2default(&addr->sin6_addr); + } +#endif if (!IN6_IS_ADDR_UNSPECIFIED(&addr->sin6_addr) && (ia = ifa_ifwithaddr((struct sockaddr *)addr)) == 0) return EADDRNOTAVAIL; if (ia && ((struct in6_ifaddr *)ia)->ia6_flags & (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY| IN6_IFF_DETACHED|IN6_IFF_DEPRECATED)) { return(EADDRNOTAVAIL); } inp->in6p_laddr = addr->sin6_addr; return 0; } static int rip6_connect(struct socket *so, struct sockaddr *nam, struct proc *p) { struct inpcb *inp = sotoinpcb(so); struct sockaddr_in6 *addr = (struct sockaddr_in6 *)nam; struct in6_addr *in6a = NULL; int error = 0; +#ifdef ENABLE_DEFAULT_SCOPE + struct sockaddr_in6 tmp; +#endif if (nam->sa_len != sizeof(*addr)) return EINVAL; if (TAILQ_EMPTY(&ifnet)) return EADDRNOTAVAIL; if (addr->sin6_family != AF_INET6) return EAFNOSUPPORT; - +#ifdef ENABLE_DEFAULT_SCOPE + if (addr->sin6_scope_id == 0) { /* not change if specified */ + /* avoid overwrites */ + tmp = *addr; + addr = &tmp; + addr->sin6_scope_id = scope6_addr2default(&addr->sin6_addr); + } +#endif /* Source address selection. XXX: need pcblookup? */ in6a = in6_selectsrc(addr, inp->in6p_outputopts, inp->in6p_moptions, &inp->in6p_route, &inp->in6p_laddr, &error); if (in6a == NULL) return (error ? error : EADDRNOTAVAIL); inp->in6p_laddr = *in6a; inp->in6p_faddr = addr->sin6_addr; soisconnected(so); return 0; } static int rip6_shutdown(struct socket *so) { socantsendmore(so); return 0; } static int rip6_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam, struct mbuf *control, struct proc *p) { struct inpcb *inp = sotoinpcb(so); struct sockaddr_in6 tmp; struct sockaddr_in6 *dst; + /* always copy sockaddr to avoid overwrites */ if (so->so_state & SS_ISCONNECTED) { if (nam) { m_freem(m); return EISCONN; } /* XXX */ bzero(&tmp, sizeof(tmp)); tmp.sin6_family = AF_INET6; tmp.sin6_len = sizeof(struct sockaddr_in6); bcopy(&inp->in6p_faddr, &tmp.sin6_addr, sizeof(struct in6_addr)); dst = &tmp; } else { if (nam == NULL) { m_freem(m); return ENOTCONN; } - dst = (struct sockaddr_in6 *)nam; + tmp = *(struct sockaddr_in6 *)nam; + dst = &tmp; } +#ifdef ENABLE_DEFAULT_SCOPE + if (dst->sin6_scope_id == 0) { /* not change if specified */ + dst->sin6_scope_id = scope6_addr2default(&dst->sin6_addr); + } +#endif return rip6_output(m, so, dst, control); } struct pr_usrreqs rip6_usrreqs = { rip6_abort, pru_accept_notsupp, rip6_attach, rip6_bind, rip6_connect, pru_connect2_notsupp, in6_control, rip6_detach, rip6_disconnect, pru_listen_notsupp, in6_setpeeraddr, pru_rcvd_notsupp, pru_rcvoob_notsupp, rip6_send, pru_sense_null, rip6_shutdown, in6_setsockaddr, sosend, soreceive, sopoll }; Index: head/sys/netinet6/route6.c =================================================================== --- head/sys/netinet6/route6.c (revision 62586) +++ head/sys/netinet6/route6.c (revision 62587) @@ -1,155 +1,205 @@ +/* $FreeBSD$ */ +/* $KAME: route6.c,v 1.15 2000/06/23 16:18:20 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ +#include "opt_inet.h" +#include "opt_inet6.h" + #include #include #include +#include #include #include -#include #include -#include +#include #include #include -static int ip6_rthdr0 __P((struct mbuf *, struct ip6_hdr *, struct ip6_rthdr0 *)); +static int ip6_rthdr0 __P((struct mbuf *, struct ip6_hdr *, + struct ip6_rthdr0 *)); int route6_input(mp, offp, proto) struct mbuf **mp; int *offp, proto; /* proto is unused */ { register struct ip6_hdr *ip6; register struct mbuf *m = *mp; register struct ip6_rthdr *rh; int off = *offp, rhlen; +#ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, off, sizeof(*rh), IPPROTO_DONE); ip6 = mtod(m, struct ip6_hdr *); rh = (struct ip6_rthdr *)((caddr_t)ip6 + off); +#else + ip6 = mtod(m, struct ip6_hdr *); + IP6_EXTHDR_GET(rh, struct ip6_rthdr *, m, off, sizeof(*rh)); + if (rh == NULL) { + ip6stat.ip6s_tooshort++; + return IPPROTO_DONE; + } +#endif - switch(rh->ip6r_type) { - case IPV6_RTHDR_TYPE_0: - rhlen = (rh->ip6r_len + 1) << 3; - IP6_EXTHDR_CHECK(m, off, rhlen, IPPROTO_DONE); - if (ip6_rthdr0(m, ip6, (struct ip6_rthdr0 *)rh)) - return(IPPROTO_DONE); - break; - default: - /* unknown routing type */ - if (rh->ip6r_segleft == 0) { - rhlen = (rh->ip6r_len + 1) << 3; - break; /* Final dst. Just ignore the header. */ - } - ip6stat.ip6s_badoptions++; - icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, - (caddr_t)&rh->ip6r_type - (caddr_t)ip6); - return(IPPROTO_DONE); + switch (rh->ip6r_type) { + case IPV6_RTHDR_TYPE_0: + rhlen = (rh->ip6r_len + 1) << 3; +#ifndef PULLDOWN_TEST + /* + * note on option length: + * due to IP6_EXTHDR_CHECK assumption, we cannot handle + * very big routing header (max rhlen == 2048). + */ + IP6_EXTHDR_CHECK(m, off, rhlen, IPPROTO_DONE); +#else + /* + * note on option length: + * maximum rhlen: 2048 + * max mbuf m_pulldown can handle: MCLBYTES == usually 2048 + * so, here we are assuming that m_pulldown can handle + * rhlen == 2048 case. this may not be a good thing to + * assume - we may want to avoid pulling it up altogether. + */ + IP6_EXTHDR_GET(rh, struct ip6_rthdr *, m, off, rhlen); + if (rh == NULL) { + ip6stat.ip6s_tooshort++; + return IPPROTO_DONE; + } +#endif + if (ip6_rthdr0(m, ip6, (struct ip6_rthdr0 *)rh)) + return(IPPROTO_DONE); + break; + default: + /* unknown routing type */ + if (rh->ip6r_segleft == 0) { + rhlen = (rh->ip6r_len + 1) << 3; + break; /* Final dst. Just ignore the header. */ + } + ip6stat.ip6s_badoptions++; + icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, + (caddr_t)&rh->ip6r_type - (caddr_t)ip6); + return(IPPROTO_DONE); } *offp += rhlen; return(rh->ip6r_nxt); } /* * Type0 routing header processing */ static int ip6_rthdr0(m, ip6, rh0) struct mbuf *m; struct ip6_hdr *ip6; struct ip6_rthdr0 *rh0; { int addrs, index; struct in6_addr *nextaddr, tmpaddr; if (rh0->ip6r0_segleft == 0) return(0); if (rh0->ip6r0_len % 2 #ifdef COMPAT_RFC1883 || rh0->ip6r0_len > 46 #endif ) { /* * Type 0 routing header can't contain more than 23 addresses. * RFC 2462: this limitation was removed since stict/loose * bitmap field was deleted. */ ip6stat.ip6s_badoptions++; icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, (caddr_t)&rh0->ip6r0_len - (caddr_t)ip6); return(-1); } if ((addrs = rh0->ip6r0_len / 2) < rh0->ip6r0_segleft) { ip6stat.ip6s_badoptions++; icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, (caddr_t)&rh0->ip6r0_segleft - (caddr_t)ip6); return(-1); } index = addrs - rh0->ip6r0_segleft; rh0->ip6r0_segleft--; - nextaddr = rh0->ip6r0_addr + index; + nextaddr = ((struct in6_addr *)(rh0 + 1)) + index; + /* + * reject invalid addresses. be proactive about malicious use of + * IPv4 mapped/compat address. + * XXX need more checks? + */ if (IN6_IS_ADDR_MULTICAST(nextaddr) || - IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { + IN6_IS_ADDR_UNSPECIFIED(nextaddr) || + IN6_IS_ADDR_V4MAPPED(nextaddr) || + IN6_IS_ADDR_V4COMPAT(nextaddr)) { + ip6stat.ip6s_badoptions++; + m_freem(m); + return(-1); + } + if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) || + IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_dst) || + IN6_IS_ADDR_V4MAPPED(&ip6->ip6_dst) || + IN6_IS_ADDR_V4COMPAT(nextaddr)) { ip6stat.ip6s_badoptions++; m_freem(m); return(-1); } /* * Swap the IPv6 destination address and nextaddr. Forward the packet. */ tmpaddr = *nextaddr; *nextaddr = ip6->ip6_dst; if (IN6_IS_ADDR_LINKLOCAL(nextaddr)) nextaddr->s6_addr16[1] = 0; ip6->ip6_dst = tmpaddr; if (IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_dst)) ip6->ip6_dst.s6_addr16[1] = htons(m->m_pkthdr.rcvif->if_index); #ifdef COMPAT_RFC1883 if (rh0->ip6r0_slmap[index / 8] & (1 << (7 - (index % 8)))) ip6_forward(m, IPV6_SRCRT_NEIGHBOR); else ip6_forward(m, IPV6_SRCRT_NOTNEIGHBOR); #else ip6_forward(m, 1); #endif return(-1); /* m would be freed in ip6_forward() */ } Index: head/sys/netinet6/scope6.c =================================================================== --- head/sys/netinet6/scope6.c (nonexistent) +++ head/sys/netinet6/scope6.c (revision 62587) @@ -0,0 +1,302 @@ +/* $FreeBSD$ */ +/* $KAME: scope6.c,v 1.9 2000/05/18 15:03:26 jinmei Exp $ */ + +/* + * Copyright (C) 2000 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include + +struct scope6_id { + /* + * 16 is correspondent to 4bit multicast scope field. + * i.e. from node-local to global with some reserved/unassigned types. + */ + u_int32_t s6id_list[16]; +}; +static size_t if_indexlim = 8; +struct scope6_id *scope6_ids = NULL; + +void +scope6_ifattach(ifp) + struct ifnet *ifp; +{ + int s = splnet(); + + /* + * We have some arrays that should be indexed by if_index. + * since if_index will grow dynamically, they should grow too. + */ + if (scope6_ids == NULL || if_index >= if_indexlim) { + size_t n; + caddr_t q; + + while (if_index >= if_indexlim) + if_indexlim <<= 1; + + /* grow scope index array */ + n = if_indexlim * sizeof(struct scope6_id); + /* XXX: need new malloc type? */ + q = (caddr_t)malloc(n, M_IFADDR, M_WAITOK); + bzero(q, n); + if (scope6_ids) { + bcopy((caddr_t)scope6_ids, q, n/2); + free((caddr_t)scope6_ids, M_IFADDR); + } + scope6_ids = (struct scope6_id *)q; + } + +#define SID scope6_ids[ifp->if_index] + + /* don't initialize if called twice */ + if (SID.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL]) { + splx(s); + return; + } + + /* + * XXX: IPV6_ADDR_SCOPE_xxx macros are not standard. + * Should we rather hardcode here? + */ + SID.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = ifp->if_index; +#ifdef MULTI_SCOPE + /* by default, we don't care about scope boundary for these scopes. */ + SID.s6id_list[IPV6_ADDR_SCOPE_SITELOCAL] = 1; + SID.s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL] = 1; +#endif +#undef SID + + splx(s); +} + +int +scope6_set(ifp, idlist) + struct ifnet *ifp; + u_int32_t *idlist; +{ + int i, s; + int error = 0; + + if (scope6_ids == NULL) /* paranoid? */ + return(EINVAL); + + /* + * XXX: We need more consistency checks of the relationship among + * scopes (e.g. an organization should be larger than a site). + */ + + /* + * TODO(XXX): after setting, we should reflect the changes to + * interface addresses, routing table entries, PCB entries... + */ + + s = splnet(); + + for (i = 0; i < 16; i++) { + if (idlist[i] && + idlist[i] != scope6_ids[ifp->if_index].s6id_list[i]) { + if (i == IPV6_ADDR_SCOPE_LINKLOCAL && + idlist[i] > if_index) { + /* + * XXX: theoretically, there should be no + * relationship between link IDs and interface + * IDs, but we check the consistency for + * safety in later use. + */ + splx(s); + return(EINVAL); + } + + /* + * XXX: we must need lots of work in this case, + * but we simply set the new value in this initial + * implementation. + */ + scope6_ids[ifp->if_index].s6id_list[i] = idlist[i]; + } + } + splx(s); + + return(error); +} + +int +scope6_get(ifp, idlist) + struct ifnet *ifp; + u_int32_t *idlist; +{ + if (scope6_ids == NULL) /* paranoid? */ + return(EINVAL); + + bcopy(scope6_ids[ifp->if_index].s6id_list, idlist, + sizeof(scope6_ids[ifp->if_index].s6id_list)); + + return(0); +} + + +/* + * Get a scope of the address. Node-local, link-local, site-local or global. + */ +int +in6_addrscope(addr) +struct in6_addr *addr; +{ + int scope; + + if (addr->s6_addr8[0] == 0xfe) { + scope = addr->s6_addr8[1] & 0xc0; + + switch (scope) { + case 0x80: + return IPV6_ADDR_SCOPE_LINKLOCAL; + break; + case 0xc0: + return IPV6_ADDR_SCOPE_SITELOCAL; + break; + default: + return IPV6_ADDR_SCOPE_GLOBAL; /* just in case */ + break; + } + } + + + if (addr->s6_addr8[0] == 0xff) { + scope = addr->s6_addr8[1] & 0x0f; + + /* + * due to other scope such as reserved, + * return scope doesn't work. + */ + switch (scope) { + case IPV6_ADDR_SCOPE_NODELOCAL: + return IPV6_ADDR_SCOPE_NODELOCAL; + break; + case IPV6_ADDR_SCOPE_LINKLOCAL: + return IPV6_ADDR_SCOPE_LINKLOCAL; + break; + case IPV6_ADDR_SCOPE_SITELOCAL: + return IPV6_ADDR_SCOPE_SITELOCAL; + break; + default: + return IPV6_ADDR_SCOPE_GLOBAL; + break; + } + } + + if (bcmp(&in6addr_loopback, addr, sizeof(addr) - 1) == 0) { + if (addr->s6_addr8[15] == 1) /* loopback */ + return IPV6_ADDR_SCOPE_NODELOCAL; + if (addr->s6_addr8[15] == 0) /* unspecified */ + return IPV6_ADDR_SCOPE_LINKLOCAL; + } + + return IPV6_ADDR_SCOPE_GLOBAL; +} + +int +in6_addr2scopeid(ifp, addr) + struct ifnet *ifp; /* must not be NULL */ + struct in6_addr *addr; /* must not be NULL */ +{ + int scope = in6_addrscope(addr); + + if (scope6_ids == NULL) /* paranoid? */ + return(0); /* XXX */ + if (ifp->if_index >= if_indexlim) + return(0); /* XXX */ + +#define SID scope6_ids[ifp->if_index] + switch(scope) { + case IPV6_ADDR_SCOPE_NODELOCAL: + return(-1); /* XXX: is this an appropriate value? */ + + case IPV6_ADDR_SCOPE_LINKLOCAL: + return(SID.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL]); + + case IPV6_ADDR_SCOPE_SITELOCAL: + return(SID.s6id_list[IPV6_ADDR_SCOPE_SITELOCAL]); + + case IPV6_ADDR_SCOPE_ORGLOCAL: + return(SID.s6id_list[IPV6_ADDR_SCOPE_ORGLOCAL]); + + default: + return(0); /* XXX: treat as global. */ + } +#undef SID +} + +void +scope6_setdefault(ifp) + struct ifnet *ifp; /* note that this might be NULL */ +{ + /* + * Currently, this function just set the default "link" according to + * the given interface. + * We might eventually have to separate the notion of "link" from + * "interface" and provide a user interface to set the default. + */ + if (ifp) { + scope6_ids[0].s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = + ifp->if_index; + } + else + scope6_ids[0].s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = 0; +} + +int +scope6_get_default(idlist) + u_int32_t *idlist; +{ + if (scope6_ids == NULL) /* paranoid? */ + return(EINVAL); + + bcopy(scope6_ids[0].s6id_list, idlist, + sizeof(scope6_ids[0].s6id_list)); + + return(0); +} + +u_int32_t +scope6_addr2default(addr) + struct in6_addr *addr; +{ + return(scope6_ids[0].s6id_list[in6_addrscope(addr)]); +} Property changes on: head/sys/netinet6/scope6.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: head/sys/netinet6/scope6_var.h =================================================================== --- head/sys/netinet6/scope6_var.h (nonexistent) +++ head/sys/netinet6/scope6_var.h (revision 62587) @@ -0,0 +1,46 @@ +/* $FreeBSD$ */ +/* $KAME: scope6_var.h,v 1.4 2000/05/18 15:03:27 jinmei Exp $ */ + +/* + * Copyright (C) 2000 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _NETINET6_SCOPE6_VAR_H_ +#define _NETINET6_SCOPE6_VAR_H_ + +#ifdef _KERNEL +void scope6_ifattach __P((struct ifnet *)); +int scope6_set __P((struct ifnet *, u_int32_t *)); +int scope6_get __P((struct ifnet *, u_int32_t *)); +void scope6_setdefault __P((struct ifnet *)); +int scope6_get_default __P((u_int32_t *)); +u_int32_t scope6_in6_addrscope __P((struct in6_addr *)); +u_int32_t scope6_addr2default __P((struct in6_addr *)); +#endif /* _KERNEL */ + +#endif /* _NETINET6_SCOPE6_VAR_H_ */ Property changes on: head/sys/netinet6/scope6_var.h ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: head/sys/netinet6/tcp6_var.h =================================================================== --- head/sys/netinet6/tcp6_var.h (revision 62586) +++ head/sys/netinet6/tcp6_var.h (revision 62587) @@ -1,88 +1,88 @@ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * Copyright (c) 1982, 1986, 1993, 1994, 1995 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)tcp_var.h 8.4 (Berkeley) 5/24/95 * $FreeBSD$ */ #ifndef _NETINET_TCP6_VAR_H_ -#define _NETINET_TCP6_VAR_H_ +#define _NETINET_TCP6_VAR_H_ #ifdef _KERNEL #ifdef SYSCTL_DECL SYSCTL_DECL(_net_inet6_tcp6); #endif extern int tcp_v6mssdflt; /* XXX */ struct ip6_hdr; void tcp6_ctlinput __P((int, struct sockaddr *, void *)); void tcp6_init __P((void)); int tcp6_input __P((struct mbuf **, int *, int)); struct rtentry *tcp_rtlookup6 __P((struct inpcb *)); extern struct pr_usrreqs tcp6_usrreqs; #endif /* _KERNEL */ #endif /* _NETINET_TCP6_VAR_H_ */ Index: head/sys/netinet6/udp6_output.c =================================================================== --- head/sys/netinet6/udp6_output.c (nonexistent) +++ head/sys/netinet6/udp6_output.c (revision 62587) @@ -0,0 +1,285 @@ +/* $FreeBSD$ */ +/* $KAME: udp6_output.c,v 1.14 2000/06/13 10:31:23 itojun Exp $ */ + +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Copyright (c) 1982, 1986, 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)udp_var.h 8.1 (Berkeley) 6/10/93 + */ + +#include "opt_ipsec.h" +#include "opt_inet.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef IPSEC +#include +#ifdef INET6 +#include +#endif +#endif /*IPSEC*/ + +#include "faith.h" + +#include + +/* + * UDP protocol inplementation. + * Per RFC 768, August, 1980. + */ + +#define in6pcb inpcb +#define udp6stat udpstat +#define udp6s_opackets udps_opackets + +int +udp6_output(in6p, m, addr6, control, p) + register struct in6pcb *in6p; + register struct mbuf *m; + struct mbuf *control; + struct sockaddr *addr6; + struct proc *p; +{ + register u_int32_t ulen = m->m_pkthdr.len; + u_int32_t plen = sizeof(struct udphdr) + ulen; + struct ip6_hdr *ip6; + struct udphdr *udp6; + struct in6_addr *laddr, *faddr; + u_short fport; + int error = 0; + struct ip6_pktopts opt, *stickyopt = in6p->in6p_outputopts; + int priv; + int af, hlen; + int flags; + struct sockaddr_in6 tmp; + + priv = 0; + if (p && !suser(p)) + priv = 1; + if (control) { + if ((error = ip6_setpktoptions(control, &opt, priv)) != 0) + goto release; + in6p->in6p_outputopts = &opt; + } + + if (addr6) { + /* + * IPv4 version of udp_output calls in_pcbconnect in this case, + * which needs splnet and affects performance. + * Since we saw no essential reason for calling in_pcbconnect, + * we get rid of such kind of logic, and call in6_selectsrc + * and in6_pcbsetport in order to fill in the local address + * and the local port. + */ + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)addr6; + if (sin6->sin6_port == 0) { + error = EADDRNOTAVAIL; + goto release; + } + + if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr)) { + error = EISCONN; + goto release; + } + + /* protect *sin6 from overwrites */ + tmp = *sin6; + sin6 = &tmp; + + faddr = &sin6->sin6_addr; + fport = sin6->sin6_port; /* allow 0 port */ + + /* KAME hack: embed scopeid */ + if (in6_embedscope(&sin6->sin6_addr, sin6, in6p, NULL) != 0) { + error = EINVAL; + goto release; + } + + if (!IN6_IS_ADDR_V4MAPPED(faddr)) { + laddr = in6_selectsrc(sin6, in6p->in6p_outputopts, + in6p->in6p_moptions, + &in6p->in6p_route, + &in6p->in6p_laddr, &error); + } else + laddr = &in6p->in6p_laddr; /*XXX*/ + if (laddr == NULL) { + if (error == 0) + error = EADDRNOTAVAIL; + goto release; + } + if (in6p->in6p_lport == 0 && + (error = in6_pcbsetport(laddr, in6p, p)) != 0) + goto release; + } else { + if (IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr)) { + error = ENOTCONN; + goto release; + } + laddr = &in6p->in6p_laddr; + faddr = &in6p->in6p_faddr; + fport = in6p->in6p_fport; + } + + if (!IN6_IS_ADDR_V4MAPPED(faddr)) { + af = AF_INET6; + hlen = sizeof(struct ip6_hdr); + } else { + af = AF_INET; + hlen = sizeof(struct ip); + } + + /* + * Calculate data length and get a mbuf + * for UDP and IP6 headers. + */ + M_PREPEND(m, hlen + sizeof(struct udphdr), M_DONTWAIT); + if (m == 0) { + error = ENOBUFS; + goto release; + } + + /* + * Stuff checksum and output datagram. + */ + udp6 = (struct udphdr *)(mtod(m, caddr_t) + hlen); + udp6->uh_sport = in6p->in6p_lport; /* lport is always set in the PCB */ + udp6->uh_dport = fport; + if (plen <= 0xffff) + udp6->uh_ulen = htons((u_short)plen); + else + udp6->uh_ulen = 0; + udp6->uh_sum = 0; + + switch (af) { + case AF_INET6: + ip6 = mtod(m, struct ip6_hdr *); + ip6->ip6_flow = in6p->in6p_flowinfo & IPV6_FLOWINFO_MASK; + ip6->ip6_vfc &= ~IPV6_VERSION_MASK; + ip6->ip6_vfc |= IPV6_VERSION; +#if 0 /* ip6_plen will be filled in ip6_output. */ + ip6->ip6_plen = htons((u_short)plen); +#endif + ip6->ip6_nxt = IPPROTO_UDP; + ip6->ip6_hlim = in6_selecthlim(in6p, + in6p->in6p_route.ro_rt ? + in6p->in6p_route.ro_rt->rt_ifp : NULL); + ip6->ip6_src = *laddr; + ip6->ip6_dst = *faddr; + + if ((udp6->uh_sum = in6_cksum(m, IPPROTO_UDP, + sizeof(struct ip6_hdr), plen)) == 0) { + udp6->uh_sum = 0xffff; + } + + flags = 0; + + udp6stat.udp6s_opackets++; +#ifdef IPSEC + ipsec_setsocket(m, in6p->in6p_socket); +#endif /*IPSEC*/ + error = ip6_output(m, in6p->in6p_outputopts, &in6p->in6p_route, + flags, in6p->in6p_moptions, NULL); + break; + case AF_INET: + error = EAFNOSUPPORT; + goto release; + } + goto releaseopt; + +release: + m_freem(m); + +releaseopt: + if (control) { + in6p->in6p_outputopts = stickyopt; + m_freem(control); + } + return(error); +} Property changes on: head/sys/netinet6/udp6_output.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: head/sys/netinet6/udp6_usrreq.c =================================================================== --- head/sys/netinet6/udp6_usrreq.c (revision 62586) +++ head/sys/netinet6/udp6_usrreq.c (revision 62587) @@ -1,837 +1,757 @@ +/* $FreeBSD$ */ +/* $KAME: udp6_usrreq.c,v 1.11 2000/06/18 06:23:06 jinmei Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Copyright (c) 1982, 1986, 1989, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)udp_var.h 8.1 (Berkeley) 6/10/93 - * $FreeBSD$ */ +#include "opt_inet.h" +#include "opt_inet6.h" #include "opt_ipsec.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include -#include +#include #include #include #ifdef IPSEC #include #include #endif /*IPSEC*/ #include "faith.h" /* * UDP protocol inplementation. * Per RFC 768, August, 1980. */ extern struct protosw inetsw[]; static int in6_mcmatch __P((struct inpcb *, struct in6_addr *, struct ifnet *)); static int udp6_detach __P((struct socket *so)); static int in6_mcmatch(in6p, ia6, ifp) struct inpcb *in6p; register struct in6_addr *ia6; struct ifnet *ifp; { struct ip6_moptions *im6o = in6p->in6p_moptions; struct in6_multi_mship *imm; if (im6o == NULL) return 0; for (imm = im6o->im6o_memberships.lh_first; imm != NULL; imm = imm->i6mm_chain.le_next) { if ((ifp == NULL || imm->i6mm_maddr->in6m_ifp == ifp) && IN6_ARE_ADDR_EQUAL(&imm->i6mm_maddr->in6m_addr, ia6)) return 1; } return 0; } int udp6_input(mp, offp, proto) struct mbuf **mp; int *offp, proto; { struct mbuf *m = *mp; register struct ip6_hdr *ip6; register struct udphdr *uh; register struct inpcb *in6p; struct mbuf *opts = 0; int off = *offp; int plen, ulen; struct sockaddr_in6 udp_in6; #if defined(NFAITH) && 0 < NFAITH if (m->m_pkthdr.rcvif) { if (m->m_pkthdr.rcvif->if_type == IFT_FAITH) { /* XXX send icmp6 host/port unreach? */ m_freem(m); return IPPROTO_DONE; } } #endif udpstat.udps_ipackets++; IP6_EXTHDR_CHECK(m, off, sizeof(struct udphdr), IPPROTO_DONE); ip6 = mtod(m, struct ip6_hdr *); plen = ntohs(ip6->ip6_plen) - off + sizeof(*ip6); uh = (struct udphdr *)((caddr_t)ip6 + off); ulen = ntohs((u_short)uh->uh_ulen); if (plen != ulen) { udpstat.udps_badlen++; goto bad; } /* * Checksum extended UDP header and data. */ if (uh->uh_sum == 0) udpstat.udps_nosum++; else if (in6_cksum(m, IPPROTO_UDP, off, ulen) != 0) { udpstat.udps_badsum++; goto bad; } if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { struct inpcb *last; /* * Deliver a multicast datagram to all sockets * for which the local and remote addresses and ports match * those of the incoming datagram. This allows more than * one process to receive multicasts on the same port. * (This really ought to be done for unicast datagrams as * well, but that would cause problems with existing * applications that open both address-specific sockets and * a wildcard socket listening to the same port -- they would * end up receiving duplicates of every unicast datagram. * Those applications open the multiple sockets to overcome an * inadequacy of the UDP socket interface, but for backwards * compatibility we avoid the problem here rather than * fixing the interface. Maybe 4.5BSD will remedy this?) */ /* * In a case that laddr should be set to the link-local * address (this happens in RIPng), the multicast address * specified in the received packet does not match with * laddr. To cure this situation, the matching is relaxed * if the receiving interface is the same as one specified * in the socket and if the destination multicast address * matches one of the multicast groups specified in the socket. */ /* * Construct sockaddr format source address. */ init_sin6(&udp_in6, m); /* general init */ udp_in6.sin6_port = uh->uh_sport; /* * KAME note: usually we drop udphdr from mbuf here. * We need udphdr for IPsec processing so we do that later. */ /* * Locate pcb(s) for datagram. * (Algorithm copied from raw_intr().) */ last = NULL; LIST_FOREACH(in6p, &udb, inp_list) { if ((in6p->inp_vflag & INP_IPV6) == 0) continue; if (in6p->in6p_lport != uh->uh_dport) continue; if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr)) { if (!IN6_ARE_ADDR_EQUAL(&in6p->in6p_laddr, &ip6->ip6_dst) && !in6_mcmatch(in6p, &ip6->ip6_dst, m->m_pkthdr.rcvif)) continue; } if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr)) { if (!IN6_ARE_ADDR_EQUAL(&in6p->in6p_faddr, &ip6->ip6_src) || in6p->in6p_fport != uh->uh_sport) continue; } if (last != NULL) { struct mbuf *n; #ifdef IPSEC /* * Check AH/ESP integrity. */ if (ipsec6_in_reject_so(m, last->inp_socket)) ipsec6stat.in_polvio++; /* do not inject data into pcb */ else #endif /*IPSEC*/ if ((n = m_copy(m, 0, M_COPYALL)) != NULL) { /* * KAME NOTE: do not * m_copy(m, offset, ...) above. * sbappendaddr() expects M_PKTHDR, * and m_copy() will copy M_PKTHDR * only if offset is 0. */ if (last->in6p_flags & IN6P_CONTROLOPTS || last->in6p_socket->so_options & SO_TIMESTAMP) ip6_savecontrol(last, &opts, ip6, n); m_adj(n, off + sizeof(struct udphdr)); if (sbappendaddr(&last->in6p_socket->so_rcv, (struct sockaddr *)&udp_in6, n, opts) == 0) { m_freem(n); if (opts) m_freem(opts); udpstat.udps_fullsock++; } else sorwakeup(last->in6p_socket); opts = 0; } } last = in6p; /* * Don't look for additional matches if this one does * not have either the SO_REUSEPORT or SO_REUSEADDR * socket options set. This heuristic avoids searching * through all pcbs in the common case of a non-shared * port. It assumes that an application will never * clear these options after setting them. */ if ((last->in6p_socket->so_options & (SO_REUSEPORT|SO_REUSEADDR)) == 0) break; } if (last == NULL) { /* * No matching pcb found; discard datagram. * (No need to send an ICMP Port Unreachable * for a broadcast or multicast datgram.) */ udpstat.udps_noport++; udpstat.udps_noportmcast++; goto bad; } #ifdef IPSEC /* * Check AH/ESP integrity. */ if (ipsec6_in_reject_so(m, last->inp_socket)) { ipsec6stat.in_polvio++; goto bad; } #endif /*IPSEC*/ if (last->in6p_flags & IN6P_CONTROLOPTS || last->in6p_socket->so_options & SO_TIMESTAMP) ip6_savecontrol(last, &opts, ip6, m); m_adj(m, off + sizeof(struct udphdr)); if (sbappendaddr(&last->in6p_socket->so_rcv, (struct sockaddr *)&udp_in6, m, opts) == 0) { udpstat.udps_fullsock++; goto bad; } sorwakeup(last->in6p_socket); return IPPROTO_DONE; } /* * Locate pcb for datagram. */ in6p = in6_pcblookup_hash(&udbinfo, &ip6->ip6_src, uh->uh_sport, &ip6->ip6_dst, uh->uh_dport, 1, m->m_pkthdr.rcvif); if (in6p == 0) { if (log_in_vain) { char buf[INET6_ADDRSTRLEN]; strcpy(buf, ip6_sprintf(&ip6->ip6_dst)); log(LOG_INFO, "Connection attempt to UDP %s:%d from %s:%d\n", buf, ntohs(uh->uh_dport), ip6_sprintf(&ip6->ip6_src), ntohs(uh->uh_sport)); } udpstat.udps_noport++; if (m->m_flags & M_MCAST) { printf("UDP6: M_MCAST is set in a unicast packet.\n"); udpstat.udps_noportmcast++; goto bad; } icmp6_error(m, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOPORT, 0); return IPPROTO_DONE; } #ifdef IPSEC /* * Check AH/ESP integrity. */ if (ipsec6_in_reject_so(m, in6p->in6p_socket)) { ipsec6stat.in_polvio++; goto bad; } #endif /*IPSEC*/ /* * Construct sockaddr format source address. * Stuff source address and datagram in user buffer. */ init_sin6(&udp_in6, m); /* general init */ udp_in6.sin6_port = uh->uh_sport; if (in6p->in6p_flags & IN6P_CONTROLOPTS || in6p->in6p_socket->so_options & SO_TIMESTAMP) ip6_savecontrol(in6p, &opts, ip6, m); m_adj(m, off + sizeof(struct udphdr)); if (sbappendaddr(&in6p->in6p_socket->so_rcv, (struct sockaddr *)&udp_in6, m, opts) == 0) { udpstat.udps_fullsock++; goto bad; } sorwakeup(in6p->in6p_socket); return IPPROTO_DONE; bad: if (m) m_freem(m); if (opts) m_freem(opts); return IPPROTO_DONE; } void udp6_ctlinput(cmd, sa, d) int cmd; struct sockaddr *sa; void *d; { register struct udphdr *uhp; struct udphdr uh; struct sockaddr_in6 sa6; struct ip6_hdr *ip6; struct mbuf *m; - int off; + int off = 0; + void (*notify) __P((struct inpcb *, int)) = udp_notify; if (sa->sa_family != AF_INET6 || sa->sa_len != sizeof(struct sockaddr_in6)) return; - off = 0; - if (!PRC_IS_REDIRECT(cmd) && - ((unsigned)cmd >= PRC_NCMDS || inet6ctlerrmap[cmd] == 0)) + if ((unsigned)cmd >= PRC_NCMDS) return; + if (PRC_IS_REDIRECT(cmd)) + notify = in6_rtchange, d = NULL; + else if (cmd == PRC_HOSTDEAD) + d = NULL; + else if (inet6ctlerrmap[cmd] == 0) + return; /* if the parameter is from icmp6, decode it. */ if (d != NULL) { struct ip6ctlparam *ip6cp = (struct ip6ctlparam *)d; m = ip6cp->ip6c_m; ip6 = ip6cp->ip6c_ip6; off = ip6cp->ip6c_off; } else { m = NULL; ip6 = NULL; } /* translate addresses into internal form */ sa6 = *(struct sockaddr_in6 *)sa; - if (m != NULL && IN6_IS_ADDR_LINKLOCAL(&sa6.sin6_addr)) + if (IN6_IS_ADDR_LINKLOCAL(&sa6.sin6_addr) && m && m->m_pkthdr.rcvif) sa6.sin6_addr.s6_addr16[1] = htons(m->m_pkthdr.rcvif->if_index); if (ip6) { /* * XXX: We assume that when IPV6 is non NULL, * M and OFF are valid. */ struct in6_addr s; /* translate addresses into internal form */ memcpy(&s, &ip6->ip6_src, sizeof(s)); if (IN6_IS_ADDR_LINKLOCAL(&s)) s.s6_addr16[1] = htons(m->m_pkthdr.rcvif->if_index); if (m->m_len < off + sizeof(uh)) { /* * this should be rare case, * so we compromise on this copy... */ m_copydata(m, off, sizeof(uh), (caddr_t)&uh); uhp = &uh; } else uhp = (struct udphdr *)(mtod(m, caddr_t) + off); (void) in6_pcbnotify(&udb, (struct sockaddr *)&sa6, uhp->uh_dport, &s, - uhp->uh_sport, cmd, udp_notify); + uhp->uh_sport, cmd, notify); } else (void) in6_pcbnotify(&udb, (struct sockaddr *)&sa6, 0, - &zeroin6_addr, 0, cmd, udp_notify); + &zeroin6_addr, 0, cmd, notify); } static int udp6_getcred(SYSCTL_HANDLER_ARGS) { struct sockaddr_in6 addrs[2]; struct inpcb *inp; int error, s; error = suser(req->p); if (error) return (error); + + if (req->newlen != sizeof(addrs)) + return (EINVAL); + if (req->oldlen != sizeof(struct ucred)) + return (EINVAL); error = SYSCTL_IN(req, addrs, sizeof(addrs)); if (error) return (error); s = splnet(); inp = in6_pcblookup_hash(&udbinfo, &addrs[1].sin6_addr, addrs[1].sin6_port, &addrs[0].sin6_addr, addrs[0].sin6_port, 1, NULL); if (!inp || !inp->inp_socket) { error = ENOENT; goto out; } error = SYSCTL_OUT(req, inp->inp_socket->so_cred, sizeof(struct ucred)); out: splx(s); return (error); } SYSCTL_PROC(_net_inet6_udp6, OID_AUTO, getcred, CTLTYPE_OPAQUE|CTLFLAG_RW, 0, 0, udp6_getcred, "S,ucred", "Get the ucred of a UDP6 connection"); -int -udp6_output(in6p, m, addr6, control, p) - register struct inpcb *in6p; - struct mbuf *m; - struct sockaddr *addr6; - struct mbuf *control; - struct proc *p; -{ - register int ulen = m->m_pkthdr.len; - int plen = sizeof(struct udphdr) + ulen; - struct ip6_hdr *ip6; - struct udphdr *udp6; - struct in6_addr laddr6; - int s = 0, error = 0; - struct ip6_pktopts opt, *stickyopt = in6p->in6p_outputopts; - - if (control) { - if ((error = ip6_setpktoptions(control, &opt, suser(p))) != 0) - goto release; - in6p->in6p_outputopts = &opt; - } - - if (addr6) { - laddr6 = in6p->in6p_laddr; - if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr)) { - error = EISCONN; - goto release; - } - /* - * Must block input while temporarily connected. - */ - s = splnet(); - /* - * XXX: the user might want to overwrite the local address - * via an ancillary data. - */ - bzero(&in6p->in6p_laddr, sizeof(struct in6_addr)); - error = in6_pcbconnect(in6p, addr6, p); - if (error) { - splx(s); - goto release; - } - } else { - if (IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr)) { - error = ENOTCONN; - goto release; - } - } - /* - * Calculate data length and get a mbuf - * for UDP and IP6 headers. - */ - M_PREPEND(m, sizeof(struct ip6_hdr) + sizeof(struct udphdr), - M_DONTWAIT); - if (m == 0) { - error = ENOBUFS; - if (addr6) - splx(s); - goto release; - } - - /* - * Stuff checksum and output datagram. - */ - ip6 = mtod(m, struct ip6_hdr *); - ip6->ip6_flow = (ip6->ip6_flow & ~IPV6_FLOWINFO_MASK) | - (in6p->in6p_flowinfo & IPV6_FLOWINFO_MASK); - ip6->ip6_vfc = (ip6->ip6_vfc & ~IPV6_VERSION_MASK) | - (IPV6_VERSION & IPV6_VERSION_MASK); - /* ip6_plen will be filled in ip6_output. */ - ip6->ip6_nxt = IPPROTO_UDP; - ip6->ip6_hlim = in6_selecthlim(in6p, - in6p->in6p_route.ro_rt ? - in6p->in6p_route.ro_rt->rt_ifp : - NULL); - ip6->ip6_src = in6p->in6p_laddr; - ip6->ip6_dst = in6p->in6p_faddr; - - udp6 = (struct udphdr *)(ip6 + 1); - udp6->uh_sport = in6p->in6p_lport; - udp6->uh_dport = in6p->in6p_fport; - udp6->uh_ulen = htons((u_short)plen); - udp6->uh_sum = 0; - - if ((udp6->uh_sum = in6_cksum(m, IPPROTO_UDP, - sizeof(struct ip6_hdr), plen)) == 0) { - udp6->uh_sum = 0xffff; - } - - udpstat.udps_opackets++; - -#ifdef IPSEC - m->m_pkthdr.rcvif = (struct ifnet *)in6p->in6p_socket; -#endif /*IPSEC*/ - error = ip6_output(m, in6p->in6p_outputopts, &in6p->in6p_route, - IPV6_SOCKINMRCVIF, in6p->in6p_moptions, NULL); - - if (addr6) { - in6_pcbdisconnect(in6p); - in6p->in6p_laddr = laddr6; - splx(s); - } - goto releaseopt; - -release: - m_freem(m); - -releaseopt: - if (control) { - in6p->in6p_outputopts = stickyopt; - m_freem(control); - } - return(error); -} - static int udp6_abort(struct socket *so) { struct inpcb *inp; int s; inp = sotoinpcb(so); if (inp == 0) return EINVAL; /* ??? possible? panic instead? */ soisdisconnected(so); s = splnet(); in6_pcbdetach(inp); splx(s); return 0; } static int udp6_attach(struct socket *so, int proto, struct proc *p) { struct inpcb *inp; int s, error; inp = sotoinpcb(so); if (inp != 0) return EINVAL; if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) { error = soreserve(so, udp_sendspace, udp_recvspace); if (error) return error; } s = splnet(); error = in_pcballoc(so, &udbinfo, p); splx(s); if (error) return error; inp = (struct inpcb *)so->so_pcb; inp->inp_vflag |= INP_IPV6; inp->in6p_hops = -1; /* use kernel default */ inp->in6p_cksum = -1; /* just to be sure */ /* * XXX: ugly!! * IPv4 TTL initialization is necessary for an IPv6 socket as well, * because the socket may be bound to an IPv6 wildcard address, * which may match an IPv4-mapped IPv6 address. */ inp->inp_ip_ttl = ip_defttl; #ifdef IPSEC error = ipsec_init_policy(so, &inp->in6p_sp); if (error != 0) { in6_pcbdetach(inp); return (error); } #endif /*IPSEC*/ return 0; } static int udp6_bind(struct socket *so, struct sockaddr *nam, struct proc *p) { struct inpcb *inp; int s, error; inp = sotoinpcb(so); if (inp == 0) return EINVAL; inp->inp_vflag &= ~INP_IPV4; inp->inp_vflag |= INP_IPV6; if ((inp->inp_flags & IN6P_BINDV6ONLY) == 0) { struct sockaddr_in6 *sin6_p; sin6_p = (struct sockaddr_in6 *)nam; if (IN6_IS_ADDR_UNSPECIFIED(&sin6_p->sin6_addr)) inp->inp_vflag |= INP_IPV4; else if (IN6_IS_ADDR_V4MAPPED(&sin6_p->sin6_addr)) { struct sockaddr_in sin; in6_sin6_2_sin(&sin, sin6_p); inp->inp_vflag |= INP_IPV4; inp->inp_vflag &= ~INP_IPV6; s = splnet(); error = in_pcbbind(inp, (struct sockaddr *)&sin, p); splx(s); return error; } } s = splnet(); error = in6_pcbbind(inp, nam, p); splx(s); return error; } static int udp6_connect(struct socket *so, struct sockaddr *nam, struct proc *p) { struct inpcb *inp; int s, error; inp = sotoinpcb(so); if (inp == 0) return EINVAL; if ((inp->inp_flags & IN6P_BINDV6ONLY) == 0) { struct sockaddr_in6 *sin6_p; sin6_p = (struct sockaddr_in6 *)nam; if (IN6_IS_ADDR_V4MAPPED(&sin6_p->sin6_addr)) { struct sockaddr_in sin; if (inp->inp_faddr.s_addr != INADDR_ANY) return EISCONN; in6_sin6_2_sin(&sin, sin6_p); s = splnet(); error = in_pcbconnect(inp, (struct sockaddr *)&sin, p); splx(s); if (error == 0) { inp->inp_vflag |= INP_IPV4; inp->inp_vflag &= ~INP_IPV6; soisconnected(so); } return error; } } + if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) return EISCONN; s = splnet(); error = in6_pcbconnect(inp, nam, p); + if (ip6_auto_flowlabel) { + inp->in6p_flowinfo &= ~IPV6_FLOWLABEL_MASK; + inp->in6p_flowinfo |= + (htonl(ip6_flow_seq++) & IPV6_FLOWLABEL_MASK); + } splx(s); if (error == 0) { inp->inp_vflag &= ~INP_IPV4; inp->inp_vflag |= INP_IPV6; soisconnected(so); } return error; } static int udp6_detach(struct socket *so) { struct inpcb *inp; int s; inp = sotoinpcb(so); if (inp == 0) return EINVAL; s = splnet(); in6_pcbdetach(inp); splx(s); return 0; } static int udp6_disconnect(struct socket *so) { struct inpcb *inp; int s; inp = sotoinpcb(so); if (inp == 0) return EINVAL; if (inp->inp_vflag & INP_IPV4) { struct pr_usrreqs *pru; pru = inetsw[ip_protox[IPPROTO_UDP]].pr_usrreqs; return ((*pru->pru_disconnect)(so)); } if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) return ENOTCONN; s = splnet(); in6_pcbdisconnect(inp); inp->in6p_laddr = in6addr_any; splx(s); so->so_state &= ~SS_ISCONNECTED; /* XXX */ return 0; } static int udp6_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr, struct mbuf *control, struct proc *p) { struct inpcb *inp; + int error = 0; inp = sotoinpcb(so); if (inp == 0) { - m_freem(m); - return EINVAL; + error = EINVAL; + goto bad; } - if ((inp->inp_flags & IN6P_BINDV6ONLY) == 0) { + if (addr) { + if (addr->sa_len != sizeof(struct sockaddr_in6)) { + error = EINVAL; + goto bad; + } + if (addr->sa_family != AF_INET6) { + error = EAFNOSUPPORT; + goto bad; + } + } + + if (ip6_mapped_addr_on) { int hasv4addr; struct sockaddr_in6 *sin6 = 0; if (addr == 0) hasv4addr = (inp->inp_vflag & INP_IPV4); else { sin6 = (struct sockaddr_in6 *)addr; hasv4addr = IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr) ? 1 : 0; } if (hasv4addr) { struct pr_usrreqs *pru; - int error; if (sin6) in6_sin6_2_sin_in_sock(addr); pru = inetsw[ip_protox[IPPROTO_UDP]].pr_usrreqs; error = ((*pru->pru_send)(so, flags, m, addr, control, p)); /* addr will just be freed in sendit(). */ return error; } } return udp6_output(inp, m, addr, control, p); + + bad: + m_freem(m); + return(error); } struct pr_usrreqs udp6_usrreqs = { udp6_abort, pru_accept_notsupp, udp6_attach, udp6_bind, udp6_connect, pru_connect2_notsupp, in6_control, udp6_detach, udp6_disconnect, pru_listen_notsupp, in6_mapped_peeraddr, pru_rcvd_notsupp, pru_rcvoob_notsupp, udp6_send, pru_sense_null, udp_shutdown, in6_mapped_sockaddr, sosend, soreceive, sopoll }; Index: head/sys/netinet6/udp6_var.h =================================================================== --- head/sys/netinet6/udp6_var.h (revision 62586) +++ head/sys/netinet6/udp6_var.h (revision 62587) @@ -1,82 +1,82 @@ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * Copyright (c) 1982, 1986, 1989, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)udp_var.h 8.1 (Berkeley) 6/10/93 */ #ifndef _NETINET6_UDP6_VAR_H_ -#define _NETINET6_UDP6_VAR_H_ +#define _NETINET6_UDP6_VAR_H_ #ifdef _KERNEL SYSCTL_DECL(_net_inet6_udp6); extern struct pr_usrreqs udp6_usrreqs; void udp6_ctlinput __P((int, struct sockaddr *, void *)); int udp6_input __P((struct mbuf **, int *, int)); int udp6_output __P((struct inpcb *inp, struct mbuf *m, struct sockaddr *addr, struct mbuf *control, struct proc *p)); #endif #endif /*_NETINET6_UDP6_VAR_H_*/ Index: head/sys/netkey/key.c =================================================================== --- head/sys/netkey/key.c (revision 62586) +++ head/sys/netkey/key.c (revision 62587) @@ -1,5316 +1,7207 @@ +/* $FreeBSD$ */ +/* $KAME: key.c,v 1.137 2000/06/24 00:47:07 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ -/* KAME $Id: key.c,v 1.1.6.5.2.19 1999/07/22 14:09:24 itojun Exp $ */ - /* * This code is referd to RFC 2367 */ #include "opt_inet.h" #include "opt_inet6.h" #include "opt_ipsec.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #ifdef INET6 -#include +#include #include +#include +#endif /* INET6 */ + +#ifdef INET +#include +#endif +#ifdef INET6 #include #endif /* INET6 */ #include -#include #include #include #include #ifdef IPSEC_DEBUG #include #else #define KEYDEBUG(lev,arg) #endif #include -#include #ifdef INET6 #include +#endif +#include +#ifdef INET6 #include #endif #ifdef IPSEC_ESP #include #ifdef INET6 #include #endif #endif +#include -MALLOC_DEFINE(M_SECA, "key mgmt", "security associations, key management"); +#include -#if defined(IPSEC_DEBUG) +#include + +#ifndef offsetof +#define offsetof(type, member) ((size_t)(&((type *)0)->member)) +#endif +#ifndef satosin +#define satosin(s) ((struct sockaddr_in *)s) +#endif + +/* + * Note on SA reference counting: + * - SAs that are not in DEAD state will have (total external reference + 1) + * following value in reference count field. they cannot be freed and are + * referenced from SA header. + * - SAs that are in DEAD state will have (total external reference) + * in reference count field. they are ready to be freed. reference from + * SA header will be removed in key_delsav(), when the reference count + * field hits 0 (= no external reference other than from SA header. + */ + +#ifdef IPSEC_DEBUG u_int32_t key_debug_level = 0; -#endif /* defined(IPSEC_DEBUG) */ +#endif static u_int key_spi_trycnt = 1000; static u_int32_t key_spi_minval = 0x100; static u_int32_t key_spi_maxval = 0x0fffffff; /* XXX */ +static u_int32_t policy_id = 0; static u_int key_int_random = 60; /*interval to initialize randseed,1(m)*/ static u_int key_larval_lifetime = 30; /* interval to expire acquiring, 30(s)*/ static int key_blockacq_count = 10; /* counter for blocking SADB_ACQUIRE.*/ static int key_blockacq_lifetime = 20; /* lifetime for blocking SADB_ACQUIRE.*/ static u_int32_t acq_seq = 0; static int key_tick_init_random = 0; static LIST_HEAD(_sptree, secpolicy) sptree[IPSEC_DIR_MAX]; /* SPD */ static LIST_HEAD(_sahtree, secashead) sahtree; /* SAD */ static LIST_HEAD(_regtree, secreg) regtree[SADB_SATYPE_MAX + 1]; /* registed list */ #ifndef IPSEC_NONBLOCK_ACQUIRE static LIST_HEAD(_acqtree, secacq) acqtree; /* acquiring list */ #endif +static LIST_HEAD(_spacqtree, secspacq) spacqtree; /* SP acquiring list */ struct key_cb key_cb; /* search order for SAs */ static u_int saorder_state_valid[] = { - SADB_SASTATE_MATURE, SADB_SASTATE_DYING + SADB_SASTATE_DYING, SADB_SASTATE_MATURE, + /* + * This order is important because we must select a oldest SA + * for outbound processing. For inbound, This is not important. + */ }; static u_int saorder_state_alive[] = { /* except DEAD */ SADB_SASTATE_MATURE, SADB_SASTATE_DYING, SADB_SASTATE_LARVAL }; static u_int saorder_state_any[] = { SADB_SASTATE_MATURE, SADB_SASTATE_DYING, SADB_SASTATE_LARVAL, SADB_SASTATE_DEAD }; -#if defined(IPSEC_DEBUG) +static const int minsize[] = { + sizeof(struct sadb_msg), /* SADB_EXT_RESERVED */ + sizeof(struct sadb_sa), /* SADB_EXT_SA */ + sizeof(struct sadb_lifetime), /* SADB_EXT_LIFETIME_CURRENT */ + sizeof(struct sadb_lifetime), /* SADB_EXT_LIFETIME_HARD */ + sizeof(struct sadb_lifetime), /* SADB_EXT_LIFETIME_SOFT */ + sizeof(struct sadb_address), /* SADB_EXT_ADDRESS_SRC */ + sizeof(struct sadb_address), /* SADB_EXT_ADDRESS_DST */ + sizeof(struct sadb_address), /* SADB_EXT_ADDRESS_PROXY */ + sizeof(struct sadb_key), /* SADB_EXT_KEY_AUTH */ + sizeof(struct sadb_key), /* SADB_EXT_KEY_ENCRYPT */ + sizeof(struct sadb_ident), /* SADB_EXT_IDENTITY_SRC */ + sizeof(struct sadb_ident), /* SADB_EXT_IDENTITY_DST */ + sizeof(struct sadb_sens), /* SADB_EXT_SENSITIVITY */ + sizeof(struct sadb_prop), /* SADB_EXT_PROPOSAL */ + sizeof(struct sadb_supported), /* SADB_EXT_SUPPORTED_AUTH */ + sizeof(struct sadb_supported), /* SADB_EXT_SUPPORTED_ENCRYPT */ + sizeof(struct sadb_spirange), /* SADB_EXT_SPIRANGE */ + 0, /* SADB_X_EXT_KMPRIVATE */ + sizeof(struct sadb_x_policy), /* SADB_X_EXT_POLICY */ + sizeof(struct sadb_x_sa2), /* SADB_X_SA2 */ +}; +static const int maxsize[] = { + sizeof(struct sadb_msg), /* SADB_EXT_RESERVED */ + sizeof(struct sadb_sa), /* SADB_EXT_SA */ + sizeof(struct sadb_lifetime), /* SADB_EXT_LIFETIME_CURRENT */ + sizeof(struct sadb_lifetime), /* SADB_EXT_LIFETIME_HARD */ + sizeof(struct sadb_lifetime), /* SADB_EXT_LIFETIME_SOFT */ + 0, /* SADB_EXT_ADDRESS_SRC */ + 0, /* SADB_EXT_ADDRESS_DST */ + 0, /* SADB_EXT_ADDRESS_PROXY */ + 0, /* SADB_EXT_KEY_AUTH */ + 0, /* SADB_EXT_KEY_ENCRYPT */ + 0, /* SADB_EXT_IDENTITY_SRC */ + 0, /* SADB_EXT_IDENTITY_DST */ + 0, /* SADB_EXT_SENSITIVITY */ + 0, /* SADB_EXT_PROPOSAL */ + 0, /* SADB_EXT_SUPPORTED_AUTH */ + 0, /* SADB_EXT_SUPPORTED_ENCRYPT */ + sizeof(struct sadb_spirange), /* SADB_EXT_SPIRANGE */ + 0, /* SADB_X_EXT_KMPRIVATE */ + 0, /* SADB_X_EXT_POLICY */ + sizeof(struct sadb_x_sa2), /* SADB_X_SA2 */ +}; + +#ifdef SYSCTL_DECL +SYSCTL_DECL(_net_key); +#endif + +#ifdef IPSEC_DEBUG SYSCTL_INT(_net_key, KEYCTL_DEBUG_LEVEL, debug, CTLFLAG_RW, \ &key_debug_level, 0, ""); -#endif /* defined(IPSEC_DEBUG) */ +#endif /* max count of trial for the decision of spi value */ SYSCTL_INT(_net_key, KEYCTL_SPI_TRY, spi_trycnt, CTLFLAG_RW, \ &key_spi_trycnt, 0, ""); /* minimum spi value to allocate automatically. */ SYSCTL_INT(_net_key, KEYCTL_SPI_MIN_VALUE, spi_minval, CTLFLAG_RW, \ &key_spi_minval, 0, ""); /* maximun spi value to allocate automatically. */ SYSCTL_INT(_net_key, KEYCTL_SPI_MAX_VALUE, spi_maxval, CTLFLAG_RW, \ &key_spi_maxval, 0, ""); /* interval to initialize randseed */ SYSCTL_INT(_net_key, KEYCTL_RANDOM_INT, int_random, CTLFLAG_RW, \ &key_int_random, 0, ""); /* lifetime for larval SA */ -SYSCTL_INT(_net_key, KEYCTL_LARVAL_LIFETIME, larval_lifetime, CTLFLAG_RW, \ +SYSCTL_INT(_net_key, KEYCTL_LARVAL_LIFETIME, larval_lifetime, CTLFLAG_RW, \ &key_larval_lifetime, 0, ""); /* counter for blocking to send SADB_ACQUIRE to IKEd */ SYSCTL_INT(_net_key, KEYCTL_BLOCKACQ_COUNT, blockacq_count, CTLFLAG_RW, \ &key_blockacq_count, 0, ""); /* lifetime for blocking to send SADB_ACQUIRE to IKEd */ -SYSCTL_INT(_net_key, KEYCTL_BLOCKACQ_LIFETIME, blockacq_lifetime, CTLFLAG_RW, \ +SYSCTL_INT(_net_key, KEYCTL_BLOCKACQ_LIFETIME, blockacq_lifetime, CTLFLAG_RW, \ &key_blockacq_lifetime, 0, ""); -#define __LIST_FOREACH(elm, head, field) \ +static const int ipsec_esp_keymin = 256; +static const int ipsec_esp_auth = 0; +static const int ipsec_ah_keymin = 128; + +#ifndef LIST_FOREACH +#define LIST_FOREACH(elm, head, field) \ for (elm = LIST_FIRST(head); elm; elm = LIST_NEXT(elm, field)) -#define __LIST_CHAINED(elm) \ +#endif +#define __LIST_CHAINED(elm) \ (!((elm)->chain.le_next == NULL && (elm)->chain.le_prev == NULL)) +#define LIST_INSERT_TAIL(head, elm, type, field) \ +do {\ + struct type *curelm = LIST_FIRST(head); \ + if (curelm == NULL) {\ + LIST_INSERT_HEAD(head, elm, field); \ + } else { \ + while (LIST_NEXT(curelm, field)) \ + curelm = LIST_NEXT(curelm, field);\ + LIST_INSERT_AFTER(curelm, elm, field);\ + }\ +} while (0) -#define KEY_CHKSASTATE(head, sav, name) { \ +#define KEY_CHKSASTATE(head, sav, name) \ +do { \ if ((head) != (sav)) { \ printf("%s: state mismatched (TREE=%d SA=%d)\n", \ (name), (head), (sav)); \ continue; \ } \ -} +} while (0) -#define KEY_CHKSPDIR(head, sp, name) { \ +#define KEY_CHKSPDIR(head, sp, name) \ +do { \ if ((head) != (sp)) { \ printf("%s: direction mismatched (TREE=%d SP=%d), " \ "anyway continue.\n", \ (name), (head), (sp)); \ } \ -} +} while (0) -#define KMALLOC(p, t, n) \ +#if 1 +#define KMALLOC(p, t, n) \ ((p) = (t) malloc((unsigned long)(n), M_SECA, M_NOWAIT)) -#define KFREE(p) \ +#define KFREE(p) \ free((caddr_t)(p), M_SECA); +#else +#define KMALLOC(p, t, n) \ +do { \ + ((p) = (t)malloc((unsigned long)(n), M_SECA, M_NOWAIT)); \ + printf("%s %d: %p <- KMALLOC(%s, %d)\n", \ + __FILE__, __LINE__, (p), #t, n); \ +} while (0) -#define KEY_NEWBUF(dst, t, src, len) \ - ((dst) = (t)key_newbuf((src), (len))) +#define KFREE(p) \ + do { \ + printf("%s %d: %p -> KFREE()\n", __FILE__, __LINE__, (p)); \ + free((caddr_t)(p), M_SECA); \ + } while (0) +#endif /* * set parameters into secpolicyindex buffer. * Must allocate secpolicyindex buffer passed to this function. */ -#define KEY_SETSECSPIDX(_dir, s, d, ps, pd, ulp, idx) do { \ +#define KEY_SETSECSPIDX(_dir, s, d, ps, pd, ulp, idx) \ +do { \ bzero((idx), sizeof(struct secpolicyindex)); \ (idx)->dir = (_dir); \ (idx)->prefs = (ps); \ (idx)->prefd = (pd); \ (idx)->ul_proto = (ulp); \ bcopy((s), &(idx)->src, ((struct sockaddr *)(s))->sa_len); \ bcopy((d), &(idx)->dst, ((struct sockaddr *)(d))->sa_len); \ } while (0) /* * set parameters into secasindex buffer. * Must allocate secasindex buffer before calling this function. */ -#define KEY_SETSECASIDX(p, m, s, d, idx) do { \ +#define KEY_SETSECASIDX(p, m, r, s, d, idx) \ +do { \ bzero((idx), sizeof(struct secasindex)); \ (idx)->proto = (p); \ (idx)->mode = (m); \ + (idx)->reqid = (r); \ bcopy((s), &(idx)->src, ((struct sockaddr *)(s))->sa_len); \ bcopy((d), &(idx)->dst, ((struct sockaddr *)(d))->sa_len); \ } while (0) /* key statistics */ struct _keystat { u_long getspi_count; /* the avarage of count to try to get new SPI */ } keystat; -static struct secasvar *key_allocsa_policy __P((struct ipsecrequest *isr)); -static void key_freesp_so __P((struct secpolicy **sp)); -static struct secasvar *key_do_allocsa_policy __P((struct secashead *sah, - u_int state)); -static void key_delsp __P((struct secpolicy *sp)); -static struct secpolicy *key_getsp __P((struct secpolicyindex *spidx)); -static struct sadb_msg *key_spdadd __P((caddr_t *mhp)); -static struct sadb_msg *key_spddelete __P((caddr_t *mhp)); -static struct sadb_msg *key_spdflush __P((caddr_t *mhp)); -static int key_spddump __P((caddr_t *mhp, struct socket *so, int target)); -static u_int key_setdumpsp __P((struct sadb_msg *newmsg, struct secpolicy *sp, - u_int8_t type, u_int32_t seq, u_int32_t pid)); -static u_int key_getspmsglen __P((struct secpolicy *sp)); -static u_int key_getspreqmsglen __P((struct secpolicy *sp)); -static struct secashead *key_newsah __P((struct secasindex *saidx)); -static void key_delsah __P((struct secashead *sah)); -static struct secasvar *key_newsav __P((caddr_t *mhp, struct secashead *sah)); -static void key_delsav __P((struct secasvar *sav)); -static struct secashead *key_getsah __P((struct secasindex *saidx)); -static struct secasvar *key_checkspidup __P((struct secasindex *saidx, - u_int32_t spi)); -static struct secasvar *key_getsavbyspi __P((struct secashead *sah, - u_int32_t spi)); -static int key_setsaval __P((struct secasvar *sav, caddr_t *mhp)); -static u_int key_getmsglen __P((struct secasvar *sav)); -static int key_mature __P((struct secasvar *sav)); -static u_int key_setdumpsa __P((struct sadb_msg *newmsg, struct secasvar *sav, - u_int8_t type, u_int8_t satype, - u_int32_t seq, u_int32_t pid)); -static caddr_t key_setsadbmsg __P((caddr_t buf, u_int8_t type, int tlen, - u_int8_t satype, u_int32_t seq, pid_t pid, - u_int8_t reserved1, u_int8_t reserved2)); -static caddr_t key_setsadbsa __P((caddr_t buf, struct secasvar *sav)); -static caddr_t key_setsadbaddr __P((caddr_t buf, u_int16_t exttype, - struct sockaddr *saddr, u_int8_t prefixlen, u_int16_t ul_proto)); -static caddr_t key_setsadbident - __P((caddr_t buf, u_int16_t exttype, u_int16_t idtype, - caddr_t string, int stringlen, u_int64_t id)); -static caddr_t key_setsadbext __P((caddr_t p, caddr_t ext)); -static void *key_newbuf __P((void *src, u_int len)); +struct sadb_msghdr { + struct sadb_msg *msg; + struct sadb_ext *ext[SADB_EXT_MAX + 1]; + int extoff[SADB_EXT_MAX + 1]; + int extlen[SADB_EXT_MAX + 1]; +}; + +static struct secasvar *key_allocsa_policy __P((struct secasindex *)); +static void key_freesp_so __P((struct secpolicy **)); +static struct secasvar *key_do_allocsa_policy __P((struct secashead *, u_int)); +static void key_delsp __P((struct secpolicy *)); +static struct secpolicy *key_getsp __P((struct secpolicyindex *)); +static struct secpolicy *key_getspbyid __P((u_int32_t)); +static u_int32_t key_newreqid __P((void)); +static struct mbuf *key_gather_mbuf __P((struct mbuf *, + const struct sadb_msghdr *, int, int, ...)); +static int key_spdadd __P((struct socket *, struct mbuf *, + const struct sadb_msghdr *)); +static u_int32_t key_getnewspid __P((void)); +static int key_spddelete __P((struct socket *, struct mbuf *, + const struct sadb_msghdr *)); +static int key_spddelete2 __P((struct socket *, struct mbuf *, + const struct sadb_msghdr *)); +static int key_spdget __P((struct socket *, struct mbuf *, + const struct sadb_msghdr *)); +static int key_spdflush __P((struct socket *, struct mbuf *, + const struct sadb_msghdr *)); +static int key_spddump __P((struct socket *, struct mbuf *, + const struct sadb_msghdr *)); +static struct mbuf *key_setdumpsp __P((struct secpolicy *, + u_int8_t, u_int32_t, u_int32_t)); +static u_int key_getspreqmsglen __P((struct secpolicy *)); +static struct secashead *key_newsah __P((struct secasindex *)); +static void key_delsah __P((struct secashead *)); +static struct secasvar *key_newsav __P((struct mbuf *, + const struct sadb_msghdr *, struct secashead *, int *)); +static void key_delsav __P((struct secasvar *)); +static struct secashead *key_getsah __P((struct secasindex *)); +static struct secasvar *key_checkspidup __P((struct secasindex *, u_int32_t)); +static struct secasvar *key_getsavbyspi __P((struct secashead *, u_int32_t)); +static int key_setsaval __P((struct secasvar *, struct mbuf *, + const struct sadb_msghdr *)); +static int key_mature __P((struct secasvar *)); +static struct mbuf *key_setdumpsa __P((struct secasvar *, u_int8_t, + u_int8_t, u_int32_t, u_int32_t)); +static struct mbuf *key_setsadbmsg __P((u_int8_t, u_int16_t, u_int8_t, + u_int32_t, pid_t, u_int16_t)); +static struct mbuf *key_setsadbsa __P((struct secasvar *)); +static struct mbuf *key_setsadbaddr __P((u_int16_t, + struct sockaddr *, u_int8_t, u_int16_t)); +#if 0 +static struct mbuf *key_setsadbident __P((u_int16_t, u_int16_t, caddr_t, + int, u_int64_t)); +#endif +static struct mbuf *key_setsadbxsa2(u_int8_t, u_int32_t); +static struct mbuf *key_setsadbxpolicy __P((u_int16_t, u_int8_t, + u_int32_t)); +static void *key_newbuf __P((const void *, u_int)); #ifdef INET6 -static int key_ismyaddr6 __P((caddr_t addr)); +static int key_ismyaddr6 __P((struct sockaddr_in6 *)); #endif static int key_cmpsaidx_exactly - __P((struct secasindex *saidx0, struct secasindex *saidx1)); + __P((struct secasindex *, struct secasindex *)); static int key_cmpsaidx_withmode - __P((struct secasindex *saidx0, struct secasindex *saidx1)); + __P((struct secasindex *, struct secasindex *)); +static int key_cmpsaidx_withoutmode + __P((struct secasindex *, struct secasindex *)); static int key_cmpspidx_exactly - __P((struct secpolicyindex *spidx0, struct secpolicyindex *spidx1)); + __P((struct secpolicyindex *, struct secpolicyindex *)); static int key_cmpspidx_withmask - __P((struct secpolicyindex *spidx0, struct secpolicyindex *spidx1)); -static int key_bbcmp __P((caddr_t p1, caddr_t p2, u_int bits)); -static u_int16_t key_satype2proto __P((u_int8_t satype)); -static u_int8_t key_proto2satype __P((u_int16_t proto)); + __P((struct secpolicyindex *, struct secpolicyindex *)); +static int key_sockaddrcmp __P((struct sockaddr *, struct sockaddr *, int)); +static int key_bbcmp __P((caddr_t, caddr_t, u_int)); +static void key_srandom __P((void)); +static u_long key_random __P((void)); +static u_int16_t key_satype2proto __P((u_int8_t)); +static u_int8_t key_proto2satype __P((u_int16_t)); -static struct sadb_msg *key_getspi __P((caddr_t *mhp)); -static u_int32_t key_do_getnewspi __P((struct sadb_spirange *spirange, - struct secasindex *saidx)); -static struct sadb_msg *key_update __P((caddr_t *mhp)); -static struct secasvar *key_getsavbyseq __P((struct secashead *sah, - u_int32_t seq)); -static struct sadb_msg *key_add __P((caddr_t *mhp)); -static struct sadb_msg *key_getmsgbuf_x1 __P((caddr_t *mhp)); -static struct sadb_msg *key_delete __P((caddr_t *mhp)); -static struct sadb_msg *key_get __P((caddr_t *mhp)); -static int key_acquire __P((struct secasindex *saidx, - struct secpolicyindex *spidx)); -static struct secacq *key_newacq __P((struct secasindex *saidx)); -static struct secacq *key_getacq __P((struct secasindex *saidx)); -static struct secacq *key_getacqbyseq __P((u_int32_t seq)); -static struct sadb_msg *key_acquire2 __P((caddr_t *mhp)); -static struct sadb_msg *key_register __P((caddr_t *mhp, struct socket *so)); -static int key_expire __P((struct secasvar *sav)); -static struct sadb_msg *key_flush __P((caddr_t *mhp)); -static int key_dump __P((caddr_t *mhp, struct socket *so, int target)); -static void key_promisc __P((caddr_t *mhp, struct socket *so)); -static int key_sendall __P((struct sadb_msg *msg, u_int len)); -static int key_align __P((struct sadb_msg *msg, caddr_t *mhp)); -static void key_sa_chgstate __P((struct secasvar *sav, u_int8_t state)); +static int key_getspi __P((struct socket *, struct mbuf *, + const struct sadb_msghdr *)); +static u_int32_t key_do_getnewspi __P((struct sadb_spirange *, + struct secasindex *)); +static int key_update __P((struct socket *, struct mbuf *, + const struct sadb_msghdr *)); +#ifdef IPSEC_DOSEQCHECK +static struct secasvar *key_getsavbyseq __P((struct secashead *, u_int32_t)); +#endif +static int key_add __P((struct socket *, struct mbuf *, + const struct sadb_msghdr *)); +static int key_setident __P((struct secashead *, struct mbuf *, + const struct sadb_msghdr *)); +static struct mbuf *key_getmsgbuf_x1 __P((struct mbuf *, + const struct sadb_msghdr *)); +static int key_delete __P((struct socket *, struct mbuf *, + const struct sadb_msghdr *)); +static int key_get __P((struct socket *, struct mbuf *, + const struct sadb_msghdr *)); + +#ifdef IPSEC_ESP +static struct mbuf *key_getcomb_esp __P((void)); +#endif +static struct mbuf *key_getcomb_ah __P((void)); +static struct mbuf *key_getprop __P((const struct secasindex *)); + +static int key_acquire __P((struct secasindex *, struct secpolicy *)); +#ifndef IPSEC_NONBLOCK_ACQUIRE +static struct secacq *key_newacq __P((struct secasindex *)); +static struct secacq *key_getacq __P((struct secasindex *)); +static struct secacq *key_getacqbyseq __P((u_int32_t)); +#endif +static struct secspacq *key_newspacq __P((struct secpolicyindex *)); +static struct secspacq *key_getspacq __P((struct secpolicyindex *)); +static int key_acquire2 __P((struct socket *, struct mbuf *, + const struct sadb_msghdr *)); +static int key_register __P((struct socket *, struct mbuf *, + const struct sadb_msghdr *)); +static int key_expire __P((struct secasvar *)); +static int key_flush __P((struct socket *, struct mbuf *, + const struct sadb_msghdr *)); +static int key_dump __P((struct socket *, struct mbuf *, + const struct sadb_msghdr *)); +static int key_promisc __P((struct socket *, struct mbuf *, + const struct sadb_msghdr *)); +static int key_senderror __P((struct socket *, struct mbuf *, int)); +static int key_validate_ext __P((const struct sadb_ext *, int)); +static int key_align __P((struct mbuf *, struct sadb_msghdr *)); +#if 0 +static const char *key_getfqdn __P((void)); +static const char *key_getuserfqdn __P((void)); +#endif +static void key_sa_chgstate __P((struct secasvar *, u_int8_t)); +static struct mbuf *key_alloc_mbuf __P((int)); + /* %%% IPsec policy management */ /* * allocating a SP for OUTBOUND or INBOUND packet. * Must call key_freesp() later. * OUT: NULL: not found * others: found and return the pointer. */ struct secpolicy * key_allocsp(spidx, dir) struct secpolicyindex *spidx; u_int dir; { struct secpolicy *sp; int s; /* sanity check */ if (spidx == NULL) panic("key_allocsp: NULL pointer is passed.\n"); /* check direction */ switch (dir) { case IPSEC_DIR_INBOUND: case IPSEC_DIR_OUTBOUND: break; default: panic("key_allocsp: Invalid direction is passed.\n"); } /* get a SP entry */ s = splnet(); /*called from softclock()*/ KEYDEBUG(KEYDEBUG_IPSEC_DATA, printf("*** objects\n"); kdebug_secpolicyindex(spidx)); - __LIST_FOREACH(sp, &sptree[dir], chain) { + LIST_FOREACH(sp, &sptree[dir], chain) { KEYDEBUG(KEYDEBUG_IPSEC_DATA, printf("*** in SPD\n"); kdebug_secpolicyindex(&sp->spidx)); if (sp->state == IPSEC_SPSTATE_DEAD) continue; if (key_cmpspidx_withmask(&sp->spidx, spidx)) goto found; } splx(s); return NULL; found: /* sanity check */ KEY_CHKSPDIR(sp->spidx.dir, dir, "key_allocsp"); /* found a SPD entry */ sp->refcnt++; splx(s); KEYDEBUG(KEYDEBUG_IPSEC_STAMP, printf("DP key_allocsp cause refcnt++:%d SP:%p\n", sp->refcnt, sp)); return sp; } /* + * allocating a SA entry for a *OUTBOUND* packet. * checking each request entries in SP, and acquire SA if need. * OUT: 0: there are valid requests. * ENOENT: policy may be valid, but SA with REQUIRE is on acquiring. */ int -key_checkrequest(isr) +key_checkrequest(isr, saidx) struct ipsecrequest *isr; + struct secasindex *saidx; { u_int level; int error; /* sanity check */ - if (isr == NULL) + if (isr == NULL || saidx == NULL) panic("key_checkrequest: NULL pointer is passed.\n"); /* check mode */ - switch (isr->saidx.mode) { + switch (saidx->mode) { case IPSEC_MODE_TRANSPORT: case IPSEC_MODE_TUNNEL: break; case IPSEC_MODE_ANY: default: panic("key_checkrequest: Invalid policy defined.\n"); } /* get current level */ level = ipsec_get_reqlevel(isr); +#if 0 /* - * We don't allocate new SA if the state of SA in the holder is - * SADB_SASTATE_MATURE, and if this is newer one. + * We do allocate new SA only if the state of SA in the holder is + * SADB_SASTATE_DEAD. The SA for outbound must be the oldest. */ if (isr->sav != NULL) { - /* - * XXX While SA is hanging on policy request(isr), its refcnt - * can not be zero. So isr->sav->sah is valid pointer if - * isr->sav != NULL. But that may not be true in fact. - * There may be missunderstanding by myself. Anyway I set - * zero to isr->sav->sah when isr->sav is flushed. - * I must check to have conviction this issue. - */ - if (isr->sav->sah != NULL - && isr->sav != (struct secasvar *)LIST_FIRST( - &isr->sav->sah->savtree[SADB_SASTATE_MATURE])) { + if (isr->sav->sah == NULL) + panic("key_checkrequest: sah is null.\n"); + if (isr->sav == (struct secasvar *)LIST_FIRST( + &isr->sav->sah->savtree[SADB_SASTATE_DEAD])) { KEYDEBUG(KEYDEBUG_IPSEC_STAMP, printf("DP checkrequest calls free SA:%p\n", isr->sav)); key_freesav(isr->sav); + isr->sav = NULL; } + } +#else + /* + * we free any SA stashed in the IPsec request because a different + * SA may be involved each time this request is checked, either + * because new SAs are being configured, or this request is + * associated with an unconnected datagram socket, or this request + * is associated with a system default policy. + * + * The operation may have negative impact to performance. We may + * want to check cached SA carefully, rather than picking new SA + * every time. + */ + if (isr->sav != NULL) { + key_freesav(isr->sav); isr->sav = NULL; } +#endif - /* new SA allocation if no SA found. */ + /* + * new SA allocation if no SA found. + * key_allocsa_policy should allocate the oldest SA available. + * See key_do_allocsa_policy(), and draft-jenkins-ipsec-rekeying-03.txt. + */ if (isr->sav == NULL) - isr->sav = key_allocsa_policy(isr); + isr->sav = key_allocsa_policy(saidx); /* When there is SA. */ if (isr->sav != NULL) return 0; /* there is no SA */ - if ((error = key_acquire(&isr->saidx, &isr->sp->spidx)) != 0) { + if ((error = key_acquire(saidx, isr->sp)) != 0) { /* XXX What I do ? */ +#ifdef IPSEC_DEBUG printf("key_checkrequest: error %d returned " "from key_acquire.\n", error); +#endif return error; } return level == IPSEC_LEVEL_REQUIRE ? ENOENT : 0; } /* * allocating a SA for policy entry from SAD. * NOTE: searching SAD of aliving state. * OUT: NULL: not found. * others: found and return the pointer. */ static struct secasvar * -key_allocsa_policy(isr) - struct ipsecrequest *isr; +key_allocsa_policy(saidx) + struct secasindex *saidx; { struct secashead *sah; struct secasvar *sav; u_int stateidx, state; - __LIST_FOREACH(sah, &sahtree, chain) { + LIST_FOREACH(sah, &sahtree, chain) { if (sah->state == SADB_SASTATE_DEAD) continue; - if (key_cmpsaidx_withmode(&sah->saidx, &isr->saidx)) + if (key_cmpsaidx_withmode(&sah->saidx, saidx)) goto found; } return NULL; found: /* search valid state */ for (stateidx = 0; stateidx < _ARRAYLEN(saorder_state_valid); stateidx++) { state = saorder_state_valid[stateidx]; sav = key_do_allocsa_policy(sah, state); if (sav != NULL) return sav; } return NULL; } /* * searching SAD with direction, protocol, mode and state. * called by key_allocsa_policy(). * OUT: * NULL : not found * others : found, pointer to a SA. */ static struct secasvar * key_do_allocsa_policy(sah, state) struct secashead *sah; u_int state; { struct secasvar *sav, *candidate; /* initilize */ candidate = NULL; - __LIST_FOREACH(sav, &sah->savtree[state], chain) { + LIST_FOREACH(sav, &sah->savtree[state], chain) { /* sanity check */ KEY_CHKSASTATE(sav->state, state, "key_do_allocsa_policy"); /* initialize */ if (candidate == NULL) { candidate = sav; continue; } /* Which SA is the better ? */ /* sanity check 2 */ - if (candidate->lft_c == NULL || sav->lft_c == NULL) { - /*XXX do panic ? */ - printf("key_do_allocsa_policy: " + if (candidate->lft_c == NULL || sav->lft_c == NULL) + panic("key_do_allocsa_policy: " "lifetime_current is NULL.\n"); - continue; - } /* XXX What the best method is to compare ? */ - if (candidate->lft_c->sadb_lifetime_addtime < + if (candidate->lft_c->sadb_lifetime_addtime > sav->lft_c->sadb_lifetime_addtime) { candidate = sav; continue; } } if (candidate) { candidate->refcnt++; KEYDEBUG(KEYDEBUG_IPSEC_STAMP, printf("DP allocsa_policy cause " "refcnt++:%d SA:%p\n", candidate->refcnt, candidate)); } return candidate; } /* * allocating a SA entry for a *INBOUND* packet. * Must call key_freesav() later. * OUT: positive: pointer to a sav. * NULL: not found, or error occured. + * + * In the comparison, source address will be ignored for RFC2401 conformance. + * To quote, from section 4.1: + * A security association is uniquely identified by a triple consisting + * of a Security Parameter Index (SPI), an IP Destination Address, and a + * security protocol (AH or ESP) identifier. + * Note that, however, we do need to keep source address in IPsec SA. + * IKE specification and PF_KEY specification do assume that we + * keep source address in IPsec SA. We see a tricky situation here. */ struct secasvar * key_allocsa(family, src, dst, proto, spi) u_int family, proto; caddr_t src, dst; u_int32_t spi; { struct secashead *sah; struct secasvar *sav; u_int stateidx, state; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; int s; /* sanity check */ if (src == NULL || dst == NULL) panic("key_allocsa: NULL pointer is passed.\n"); /* * searching SAD. * XXX: to be checked internal IP header somewhere. Also when * IPsec tunnel packet is received. But ESP tunnel mode is * encrypted so we can't check internal IP header. */ s = splnet(); /*called from softclock()*/ - __LIST_FOREACH(sah, &sahtree, chain) { - + LIST_FOREACH(sah, &sahtree, chain) { /* search valid state */ for (stateidx = 0; stateidx < _ARRAYLEN(saorder_state_valid); stateidx++) { - state = saorder_state_valid[stateidx]; - __LIST_FOREACH(sav, &sah->savtree[state], chain) { - + LIST_FOREACH(sav, &sah->savtree[state], chain) { /* sanity check */ KEY_CHKSASTATE(sav->state, state, "key_allocsav"); if (proto != sav->sah->saidx.proto) continue; if (spi != sav->spi) continue; + if (family != sav->sah->saidx.src.ss_family || + family != sav->sah->saidx.dst.ss_family) + continue; - if (key_bbcmp(src, - _INADDRBYSA(&sav->sah->saidx.src), - _INALENBYAF(sav->sah->saidx.src.ss_family) << 3) - && key_bbcmp(dst, - _INADDRBYSA(&sav->sah->saidx.dst), - _INALENBYAF(sav->sah->saidx.dst.ss_family) << 3)) - goto found; +#if 0 /* don't check src */ + /* check src address */ + switch (family) { + case AF_INET: + bzero(&sin, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_len = sizeof(sin); + bcopy(src, &sin.sin_addr, + sizeof(sin.sin_addr)); + if (key_sockaddrcmp((struct sockaddr*)&sin, + (struct sockaddr *)&sav->sah->saidx.src, 0) != 0) + continue; + + break; + case AF_INET6: + bzero(&sin6, sizeof(sin6)); + sin6.sin6_family = AF_INET6; + sin6.sin6_len = sizeof(sin6); + bcopy(src, &sin6.sin6_addr, + sizeof(sin6.sin6_addr)); + if (IN6_IS_SCOPE_LINKLOCAL(&sin6.sin6_addr)) { + /* kame fake scopeid */ + sin6.sin6_scope_id = + ntohs(sin6.sin6_addr.s6_addr16[1]); + sin6.sin6_addr.s6_addr16[1] = 0; + } + if (key_sockaddrcmp((struct sockaddr*)&sin6, + (struct sockaddr *)&sav->sah->saidx.src, 0) != 0) + continue; + break; + default: + printf("key_allocsa: unknown address family=%d.\n", + family); + continue; + } + +#endif + /* check dst address */ + switch (family) { + case AF_INET: + bzero(&sin, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_len = sizeof(sin); + bcopy(dst, &sin.sin_addr, + sizeof(sin.sin_addr)); + if (key_sockaddrcmp((struct sockaddr*)&sin, + (struct sockaddr *)&sav->sah->saidx.dst, 0) != 0) + continue; + + break; + case AF_INET6: + bzero(&sin6, sizeof(sin6)); + sin6.sin6_family = AF_INET6; + sin6.sin6_len = sizeof(sin6); + bcopy(dst, &sin6.sin6_addr, + sizeof(sin6.sin6_addr)); + if (IN6_IS_SCOPE_LINKLOCAL(&sin6.sin6_addr)) { + /* kame fake scopeid */ + sin6.sin6_scope_id = + ntohs(sin6.sin6_addr.s6_addr16[1]); + sin6.sin6_addr.s6_addr16[1] = 0; + } + if (key_sockaddrcmp((struct sockaddr*)&sin6, + (struct sockaddr *)&sav->sah->saidx.dst, 0) != 0) + continue; + break; + default: + printf("key_allocsa: unknown address family=%d.\n", + family); + continue; + } + + goto found; } } } /* not found */ splx(s); return NULL; found: sav->refcnt++; splx(s); KEYDEBUG(KEYDEBUG_IPSEC_STAMP, printf("DP allocsa cause refcnt++:%d SA:%p\n", sav->refcnt, sav)); return sav; } /* * Must be called after calling key_allocsp(). * For both the packet without socket and key_freeso(). */ void key_freesp(sp) struct secpolicy *sp; { /* sanity check */ if (sp == NULL) panic("key_freesp: NULL pointer is passed.\n"); sp->refcnt--; KEYDEBUG(KEYDEBUG_IPSEC_STAMP, printf("DP freesp cause refcnt--:%d SP:%p\n", sp->refcnt, sp)); if (sp->refcnt == 0) key_delsp(sp); return; } /* * Must be called after calling key_allocsp(). * For the packet with socket. */ void key_freeso(so) struct socket *so; { /* sanity check */ if (so == NULL) panic("key_freeso: NULL pointer is passed.\n"); switch (so->so_proto->pr_domain->dom_family) { #ifdef INET case PF_INET: { struct inpcb *pcb = sotoinpcb(so); /* Does it have a PCB ? */ if (pcb == NULL) return; key_freesp_so(&pcb->inp_sp->sp_in); key_freesp_so(&pcb->inp_sp->sp_out); } break; #endif #ifdef INET6 case PF_INET6: { +#ifdef HAVE_NRL_INPCB + struct inpcb *pcb = sotoinpcb(so); + + /* Does it have a PCB ? */ + if (pcb == NULL) + return; + key_freesp_so(&pcb->inp_sp->sp_in); + key_freesp_so(&pcb->inp_sp->sp_out); +#else struct in6pcb *pcb = sotoin6pcb(so); /* Does it have a PCB ? */ if (pcb == NULL) return; key_freesp_so(&pcb->in6p_sp->sp_in); key_freesp_so(&pcb->in6p_sp->sp_out); +#endif } break; #endif /* INET6 */ default: +#ifdef IPSEC_DEBUG printf("key_freeso: unknown address family=%d.\n", so->so_proto->pr_domain->dom_family); +#endif return; } return; } static void key_freesp_so(sp) struct secpolicy **sp; { /* sanity check */ if (sp == NULL || *sp == NULL) panic("key_freesp_so: sp == NULL\n"); switch ((*sp)->policy) { case IPSEC_POLICY_IPSEC: KEYDEBUG(KEYDEBUG_IPSEC_STAMP, printf("DP freeso calls free SP:%p\n", *sp)); key_freesp(*sp); *sp = NULL; break; case IPSEC_POLICY_ENTRUST: case IPSEC_POLICY_BYPASS: return; default: panic("key_freesp_so: Invalid policy found %d", (*sp)->policy); } return; } /* * Must be called after calling key_allocsa(). * This function is called by key_freesp() to free some SA allocated * for a policy. */ void key_freesav(sav) struct secasvar *sav; { /* sanity check */ if (sav == NULL) panic("key_freesav: NULL pointer is passed.\n"); sav->refcnt--; KEYDEBUG(KEYDEBUG_IPSEC_STAMP, printf("DP freesav cause refcnt--:%d SA:%p SPI %d\n", sav->refcnt, sav, (u_int32_t)ntohl(sav->spi))); if (sav->refcnt == 0) key_delsav(sav); return; } /* %%% SPD management */ /* * free security policy entry. */ static void key_delsp(sp) struct secpolicy *sp; { int s; /* sanity check */ if (sp == NULL) panic("key_delsp: NULL pointer is passed.\n"); sp->state = IPSEC_SPSTATE_DEAD; if (sp->refcnt > 0) return; /* can't free */ s = splnet(); /*called from softclock()*/ /* remove from SP index */ if (__LIST_CHAINED(sp)) LIST_REMOVE(sp, chain); { struct ipsecrequest *isr = sp->req, *nextisr; while (isr != NULL) { if (isr->sav != NULL) { KEYDEBUG(KEYDEBUG_IPSEC_STAMP, printf("DP delsp calls free SA:%p\n", isr->sav)); key_freesav(isr->sav); isr->sav = NULL; } nextisr = isr->next; KFREE(isr); isr = nextisr; } } - KFREE(sp); + keydb_delsecpolicy(sp); splx(s); return; } /* * search SPD * OUT: NULL : not found * others : found, pointer to a SP. */ static struct secpolicy * key_getsp(spidx) struct secpolicyindex *spidx; { struct secpolicy *sp; /* sanity check */ if (spidx == NULL) panic("key_getsp: NULL pointer is passed.\n"); - __LIST_FOREACH(sp, &sptree[spidx->dir], chain) { + LIST_FOREACH(sp, &sptree[spidx->dir], chain) { if (sp->state == IPSEC_SPSTATE_DEAD) continue; if (key_cmpspidx_exactly(spidx, &sp->spidx)) { sp->refcnt++; return sp; } } return NULL; } +/* + * get SP by index. + * OUT: NULL : not found + * others : found, pointer to a SP. + */ +static struct secpolicy * +key_getspbyid(id) + u_int32_t id; +{ + struct secpolicy *sp; + + LIST_FOREACH(sp, &sptree[IPSEC_DIR_INBOUND], chain) { + if (sp->state == IPSEC_SPSTATE_DEAD) + continue; + if (sp->id == id) { + sp->refcnt++; + return sp; + } + } + + LIST_FOREACH(sp, &sptree[IPSEC_DIR_OUTBOUND], chain) { + if (sp->state == IPSEC_SPSTATE_DEAD) + continue; + if (sp->id == id) { + sp->refcnt++; + return sp; + } + } + + return NULL; +} + struct secpolicy * key_newsp() { struct secpolicy *newsp = NULL; - KMALLOC(newsp, struct secpolicy *, sizeof(*newsp)); - if (newsp == NULL) { - printf("key_newsp: No more memory.\n"); - return NULL; - } - bzero(newsp, sizeof(*newsp)); + newsp = keydb_newsecpolicy(); + if (!newsp) + return newsp; newsp->refcnt = 1; newsp->req = NULL; return newsp; } /* * create secpolicy structure from sadb_x_policy structure. * NOTE: `state', `secpolicyindex' in secpolicy structure are not set, * so must be set properly later. */ struct secpolicy * -key_msg2sp(xpl0) +key_msg2sp(xpl0, len, error) struct sadb_x_policy *xpl0; + size_t len; + int *error; { struct secpolicy *newsp; /* sanity check */ if (xpl0 == NULL) panic("key_msg2sp: NULL pointer was passed.\n"); + if (len < sizeof(*xpl0)) + panic("key_msg2sp: invalid length.\n"); + if (len != PFKEY_EXTLEN(xpl0)) { +#ifdef IPSEC_DEBUG + printf("key_msg2sp: Invalid msg length.\n"); +#endif + *error = EINVAL; + return NULL; + } - if ((newsp = key_newsp()) == NULL) + if ((newsp = key_newsp()) == NULL) { + *error = ENOBUFS; return NULL; + } newsp->spidx.dir = xpl0->sadb_x_policy_dir; newsp->policy = xpl0->sadb_x_policy_type; /* check policy */ switch (xpl0->sadb_x_policy_type) { case IPSEC_POLICY_DISCARD: case IPSEC_POLICY_NONE: case IPSEC_POLICY_ENTRUST: case IPSEC_POLICY_BYPASS: newsp->req = NULL; break; case IPSEC_POLICY_IPSEC: { int tlen; struct sadb_x_ipsecrequest *xisr; struct ipsecrequest **p_isr = &newsp->req; /* validity check */ - if (PFKEY_EXTLEN(xpl0) <= sizeof(*xpl0)) { + if (PFKEY_EXTLEN(xpl0) < sizeof(*xpl0)) { +#ifdef IPSEC_DEBUG printf("key_msg2sp: Invalid msg length.\n"); +#endif key_freesp(newsp); + *error = EINVAL; return NULL; } tlen = PFKEY_EXTLEN(xpl0) - sizeof(*xpl0); xisr = (struct sadb_x_ipsecrequest *)(xpl0 + 1); while (tlen > 0) { /* length check */ if (xisr->sadb_x_ipsecrequest_len < sizeof(*xisr)) { +#ifdef IPSEC_DEBUG printf("key_msg2sp: " "invalid ipsecrequest length.\n"); +#endif key_freesp(newsp); + *error = EINVAL; return NULL; } /* allocate request buffer */ KMALLOC(*p_isr, struct ipsecrequest *, sizeof(**p_isr)); if ((*p_isr) == NULL) { +#ifdef IPSEC_DEBUG printf("key_msg2sp: No more memory.\n"); +#endif key_freesp(newsp); + *error = ENOBUFS; return NULL; } bzero(*p_isr, sizeof(**p_isr)); /* set values */ (*p_isr)->next = NULL; switch (xisr->sadb_x_ipsecrequest_proto) { case IPPROTO_ESP: case IPPROTO_AH: +#if 1 /*nonstandard*/ + case IPPROTO_IPCOMP: +#endif break; default: +#ifdef IPSEC_DEBUG printf("key_msg2sp: invalid proto type=%u\n", xisr->sadb_x_ipsecrequest_proto); +#endif key_freesp(newsp); + *error = EPROTONOSUPPORT; return NULL; } (*p_isr)->saidx.proto = xisr->sadb_x_ipsecrequest_proto; switch (xisr->sadb_x_ipsecrequest_mode) { case IPSEC_MODE_TRANSPORT: case IPSEC_MODE_TUNNEL: break; case IPSEC_MODE_ANY: default: +#ifdef IPSEC_DEBUG printf("key_msg2sp: invalid mode=%u\n", xisr->sadb_x_ipsecrequest_mode); +#endif key_freesp(newsp); + *error = EINVAL; return NULL; } (*p_isr)->saidx.mode = xisr->sadb_x_ipsecrequest_mode; switch (xisr->sadb_x_ipsecrequest_level) { case IPSEC_LEVEL_DEFAULT: case IPSEC_LEVEL_USE: case IPSEC_LEVEL_REQUIRE: break; + case IPSEC_LEVEL_UNIQUE: + /* validity check */ + /* + * If range violation of reqid, kernel will + * update it, don't refuse it. + */ + if (xisr->sadb_x_ipsecrequest_reqid + > IPSEC_MANUAL_REQID_MAX) { +#ifdef IPSEC_DEBUG + printf("key_msg2sp: reqid=%d " + "range violation, " + "updated by kernel.\n", + xisr->sadb_x_ipsecrequest_reqid); +#endif + xisr->sadb_x_ipsecrequest_reqid = 0; + } + + /* allocate new reqid id if reqid is zero. */ + if (xisr->sadb_x_ipsecrequest_reqid == 0) { + u_int32_t reqid; + if ((reqid = key_newreqid()) == 0) { + key_freesp(newsp); + *error = ENOBUFS; + return NULL; + } + (*p_isr)->saidx.reqid = reqid; + xisr->sadb_x_ipsecrequest_reqid = reqid; + } else { + /* set it for manual keying. */ + (*p_isr)->saidx.reqid = + xisr->sadb_x_ipsecrequest_reqid; + } + break; + default: +#ifdef IPSEC_DEBUG printf("key_msg2sp: invalid level=%u\n", xisr->sadb_x_ipsecrequest_level); +#endif key_freesp(newsp); + *error = EINVAL; return NULL; } (*p_isr)->level = xisr->sadb_x_ipsecrequest_level; /* set IP addresses if there */ if (xisr->sadb_x_ipsecrequest_len > sizeof(*xisr)) { struct sockaddr *paddr; paddr = (struct sockaddr *)(xisr + 1); /* validity check */ if (paddr->sa_len > sizeof((*p_isr)->saidx.src)) { +#ifdef IPSEC_DEBUG printf("key_msg2sp: invalid request " "address length.\n"); +#endif key_freesp(newsp); + *error = EINVAL; return NULL; } bcopy(paddr, &(*p_isr)->saidx.src, paddr->sa_len); paddr = (struct sockaddr *)((caddr_t)paddr + paddr->sa_len); /* validity check */ if (paddr->sa_len > sizeof((*p_isr)->saidx.dst)) { +#ifdef IPSEC_DEBUG printf("key_msg2sp: invalid request " "address length.\n"); +#endif key_freesp(newsp); + *error = EINVAL; return NULL; } bcopy(paddr, &(*p_isr)->saidx.dst, paddr->sa_len); } (*p_isr)->sav = NULL; (*p_isr)->sp = newsp; /* initialization for the next. */ p_isr = &(*p_isr)->next; tlen -= xisr->sadb_x_ipsecrequest_len; /* validity check */ if (tlen < 0) { +#ifdef IPSEC_DEBUG printf("key_msg2sp: becoming tlen < 0.\n"); +#endif key_freesp(newsp); + *error = EINVAL; return NULL; } xisr = (struct sadb_x_ipsecrequest *)((caddr_t)xisr + xisr->sadb_x_ipsecrequest_len); } } break; default: +#ifdef IPSEC_DEBUG printf("key_msg2sp: invalid policy type.\n"); +#endif key_freesp(newsp); + *error = EINVAL; return NULL; } + *error = 0; return newsp; } +static u_int32_t +key_newreqid() +{ + static u_int32_t auto_reqid = IPSEC_MANUAL_REQID_MAX + 1; + + auto_reqid = (auto_reqid == ~0 + ? IPSEC_MANUAL_REQID_MAX + 1 : auto_reqid + 1); + + /* XXX should be unique check */ + + return auto_reqid; +} + /* * copy secpolicy struct to sadb_x_policy structure indicated. */ -struct sadb_x_policy * +struct mbuf * key_sp2msg(sp) struct secpolicy *sp; { struct sadb_x_policy *xpl; int tlen; caddr_t p; + struct mbuf *m; /* sanity check. */ if (sp == NULL) panic("key_sp2msg: NULL pointer was passed.\n"); tlen = key_getspreqmsglen(sp); - KMALLOC(xpl, struct sadb_x_policy *, tlen); - if (xpl == NULL) { - printf("key_sp2msg: No more memory.\n"); + m = key_alloc_mbuf(tlen); + if (!m || m->m_next) { /*XXX*/ + if (m) + m_freem(m); return NULL; } + + m->m_len = tlen; + m->m_next = NULL; + xpl = mtod(m, struct sadb_x_policy *); bzero(xpl, tlen); xpl->sadb_x_policy_len = PFKEY_UNIT64(tlen); xpl->sadb_x_policy_exttype = SADB_X_EXT_POLICY; xpl->sadb_x_policy_type = sp->policy; xpl->sadb_x_policy_dir = sp->spidx.dir; + xpl->sadb_x_policy_id = sp->id; p = (caddr_t)xpl + sizeof(*xpl); /* if is the policy for ipsec ? */ if (sp->policy == IPSEC_POLICY_IPSEC) { struct sadb_x_ipsecrequest *xisr; struct ipsecrequest *isr; for (isr = sp->req; isr != NULL; isr = isr->next) { xisr = (struct sadb_x_ipsecrequest *)p; xisr->sadb_x_ipsecrequest_proto = isr->saidx.proto; xisr->sadb_x_ipsecrequest_mode = isr->saidx.mode; xisr->sadb_x_ipsecrequest_level = isr->level; + xisr->sadb_x_ipsecrequest_reqid = isr->saidx.reqid; p += sizeof(*xisr); bcopy(&isr->saidx.src, p, isr->saidx.src.ss_len); p += isr->saidx.src.ss_len; bcopy(&isr->saidx.dst, p, isr->saidx.dst.ss_len); p += isr->saidx.src.ss_len; xisr->sadb_x_ipsecrequest_len = PFKEY_ALIGN8(sizeof(*xisr) + isr->saidx.src.ss_len + isr->saidx.dst.ss_len); } } - return xpl; + return m; } +/* m will not be freed nor modified */ +static struct mbuf * +#ifdef __STDC__ +key_gather_mbuf(struct mbuf *m, const struct sadb_msghdr *mhp, + int ndeep, int nitem, ...) +#else +key_gather_mbuf(m, mhp, ndeep, nitem, va_alist) + struct mbuf *m; + const struct sadb_msghdr *mhp; + int ndeep; + int nitem; + va_dcl +#endif +{ + va_list ap; + int idx; + int i; + struct mbuf *result = NULL, *n; + int len; + + if (m == NULL || mhp == NULL) + panic("null pointer passed to key_gather"); + + va_start(ap, nitem); + for (i = 0; i < nitem; i++) { + idx = va_arg(ap, int); + if (idx < 0 || idx > SADB_EXT_MAX) + goto fail; + /* don't attempt to pull empty extension */ + if (idx == SADB_EXT_RESERVED && mhp->msg == NULL) + continue; + if (idx != SADB_EXT_RESERVED && + (mhp->ext[idx] == NULL || mhp->extlen[idx] == 0)) + continue; + + if (idx == SADB_EXT_RESERVED) { + len = PFKEY_ALIGN8(sizeof(struct sadb_msg)); +#ifdef DIAGNOSTIC + if (len > MHLEN) + panic("assumption failed"); +#endif + MGETHDR(n, M_DONTWAIT, MT_DATA); + if (!n) + goto fail; + n->m_len = len; + n->m_next = NULL; + m_copydata(m, 0, sizeof(struct sadb_msg), + mtod(n, caddr_t)); + } else if (i < ndeep) { + len = mhp->extlen[idx]; + n = key_alloc_mbuf(len); + if (!n || n->m_next) { /*XXX*/ + if (n) + m_freem(n); + goto fail; + } + m_copydata(m, mhp->extoff[idx], mhp->extlen[idx], + mtod(n, caddr_t)); + } else { + n = m_copym(m, mhp->extoff[idx], mhp->extlen[idx], + M_DONTWAIT); + } + if (n == NULL) + goto fail; + + if (result) + m_cat(result, n); + else + result = n; + } + va_end(ap); + + if ((result->m_flags & M_PKTHDR) != 0) { + result->m_pkthdr.len = 0; + for (n = result; n; n = n->m_next) + result->m_pkthdr.len += n->m_len; + } + + return result; + +fail: + m_freem(result); + return NULL; +} + /* - * SADB_SPDADD processing + * SADB_X_SPDADD, SADB_X_SPDSETIDX or SADB_X_SPDUPDATE processing * add a entry to SP database, when received * * from the user(?). * Adding to SP database, * and send * * to the socket which was send. * - * IN: mhp: pointer to the pointer to each header. - * OUT: NULL if fail. - * other if success, return pointer to the message to send. + * SPDADD set a unique policy entry. + * SPDSETIDX like SPDADD without a part of policy requests. + * SPDUPDATE replace a unique policy entry. * + * m will always be freed. */ -static struct sadb_msg * -key_spdadd(mhp) - caddr_t *mhp; +static int +key_spdadd(so, m, mhp) + struct socket *so; + struct mbuf *m; + const struct sadb_msghdr *mhp; { - struct sadb_msg *msg0; struct sadb_address *src0, *dst0; - struct sadb_x_policy *xpl0; + struct sadb_x_policy *xpl0, *xpl; struct secpolicyindex spidx; struct secpolicy *newsp; + int error; /* sanity check */ - if (mhp == NULL || mhp[0] == NULL) + if (so == NULL || m == NULL || mhp == NULL || mhp->msg == NULL) panic("key_spdadd: NULL pointer is passed.\n"); - msg0 = (struct sadb_msg *)mhp[0]; - - if (mhp[SADB_EXT_ADDRESS_SRC] == NULL - || mhp[SADB_EXT_ADDRESS_DST] == NULL - || mhp[SADB_X_EXT_POLICY] == NULL) { + if (mhp->ext[SADB_EXT_ADDRESS_SRC] == NULL || + mhp->ext[SADB_EXT_ADDRESS_DST] == NULL || + mhp->ext[SADB_X_EXT_POLICY] == NULL) { +#ifdef IPSEC_DEBUG printf("key_spdadd: invalid message is passed.\n"); - msg0->sadb_msg_errno = EINVAL; - return NULL; +#endif + return key_senderror(so, m, EINVAL); } + if (mhp->extlen[SADB_EXT_ADDRESS_SRC] < sizeof(struct sadb_address) || + mhp->extlen[SADB_EXT_ADDRESS_DST] < sizeof(struct sadb_address) || + mhp->extlen[SADB_X_EXT_POLICY] < sizeof(struct sadb_x_policy)) { +#ifdef IPSEC_DEBUG + printf("key_spdadd: invalid message is passed.\n"); +#endif + return key_senderror(so, m, EINVAL); + } - src0 = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_SRC]; - dst0 = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_DST]; - xpl0 = (struct sadb_x_policy *)mhp[SADB_X_EXT_POLICY]; + src0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_SRC]; + dst0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_DST]; + xpl0 = (struct sadb_x_policy *)mhp->ext[SADB_X_EXT_POLICY]; /* make secindex */ + /* XXX boundary check against sa_len */ KEY_SETSECSPIDX(xpl0->sadb_x_policy_dir, src0 + 1, dst0 + 1, src0->sadb_address_prefixlen, dst0->sadb_address_prefixlen, src0->sadb_address_proto, &spidx); /* checking the direciton. */ switch (xpl0->sadb_x_policy_dir) { case IPSEC_DIR_INBOUND: case IPSEC_DIR_OUTBOUND: break; default: +#ifdef IPSEC_DEBUG printf("key_spdadd: Invalid SP direction.\n"); - msg0->sadb_msg_errno = EINVAL; - return NULL; +#endif + mhp->msg->sadb_msg_errno = EINVAL; + return 0; } - /* Is there SP in SPD ? */ - newsp = key_getsp(&spidx); - if (newsp != NULL) { - key_freesp(newsp); - printf("key_spdadd: a SP entry exists already.\n"); - msg0->sadb_msg_errno = EEXIST; - return NULL; - } - /* check policy */ /* key_spdadd() accepts DISCARD, NONE and IPSEC. */ if (xpl0->sadb_x_policy_type == IPSEC_POLICY_ENTRUST || xpl0->sadb_x_policy_type == IPSEC_POLICY_BYPASS) { +#ifdef IPSEC_DEBUG printf("key_spdadd: Invalid policy type.\n"); - msg0->sadb_msg_errno = EINVAL; - return NULL; +#endif + return key_senderror(so, m, EINVAL); } + /* policy requests are mandatory when action is ipsec. */ + if (mhp->msg->sadb_msg_type != SADB_X_SPDSETIDX + && xpl0->sadb_x_policy_type == IPSEC_POLICY_IPSEC + && mhp->extlen[SADB_X_EXT_POLICY] <= sizeof(*xpl0)) { +#ifdef IPSEC_DEBUG + printf("key_spdadd: some policy requests part required.\n"); +#endif + return key_senderror(so, m, EINVAL); + } + + /* + * checking there is SP already or not. + * If type is SPDUPDATE and no SP found, then error. + * If type is either SPDADD or SPDSETIDX and SP found, then error. + */ + newsp = key_getsp(&spidx); + if (mhp->msg->sadb_msg_type == SADB_X_SPDUPDATE) { + if (newsp == NULL) { +#ifdef IPSEC_DEBUG + printf("key_spdadd: no SP found.\n"); +#endif + return key_senderror(so, m, ENOENT); + } + + newsp->state = IPSEC_SPSTATE_DEAD; + key_freesp(newsp); + } else { + if (newsp != NULL) { + key_freesp(newsp); +#ifdef IPSEC_DEBUG + printf("key_spdadd: a SP entry exists already.\n"); +#endif + return key_senderror(so, m, EEXIST); + } + } + /* allocation new SP entry */ - if ((newsp = key_msg2sp(xpl0)) == NULL) { - msg0->sadb_msg_errno = ENOBUFS; - return NULL; + if ((newsp = key_msg2sp(xpl0, PFKEY_EXTLEN(xpl0), &error)) == NULL) { + return key_senderror(so, m, error); } + if ((newsp->id = key_getnewspid()) == 0) { + keydb_delsecpolicy(newsp); + return key_senderror(so, m, ENOBUFS); + } + + /* XXX boundary check against sa_len */ KEY_SETSECSPIDX(xpl0->sadb_x_policy_dir, src0 + 1, dst0 + 1, src0->sadb_address_prefixlen, dst0->sadb_address_prefixlen, src0->sadb_address_proto, &newsp->spidx); + /* sanity check on addr pair */ + if (((struct sockaddr *)(src0 + 1))->sa_family != + ((struct sockaddr *)(dst0+ 1))->sa_family) { + keydb_delsecpolicy(newsp); + return key_senderror(so, m, EINVAL); + } + if (((struct sockaddr *)(src0 + 1))->sa_len != + ((struct sockaddr *)(dst0+ 1))->sa_len) { + keydb_delsecpolicy(newsp); + return key_senderror(so, m, EINVAL); + } +#if 1 + if (newsp->req && newsp->req->saidx.src.ss_family) { + struct sockaddr *sa; + sa = (struct sockaddr *)(src0 + 1); + if (sa->sa_family != newsp->req->saidx.src.ss_family) { + keydb_delsecpolicy(newsp); + return key_senderror(so, m, EINVAL); + } + } + if (newsp->req && newsp->req->saidx.dst.ss_family) { + struct sockaddr *sa; + sa = (struct sockaddr *)(dst0 + 1); + if (sa->sa_family != newsp->req->saidx.dst.ss_family) { + keydb_delsecpolicy(newsp); + return key_senderror(so, m, EINVAL); + } + } +#endif + newsp->refcnt = 1; /* do not reclaim until I say I do */ newsp->state = IPSEC_SPSTATE_ALIVE; - LIST_INSERT_HEAD(&sptree[newsp->spidx.dir], newsp, chain); + LIST_INSERT_TAIL(&sptree[newsp->spidx.dir], newsp, secpolicy, chain); + /* delete the entry in spacqtree */ + if (mhp->msg->sadb_msg_type == SADB_X_SPDUPDATE) { + struct secspacq *spacq; + if ((spacq = key_getspacq(&spidx)) != NULL) { + /* reset counter in order to deletion by timehander. */ + spacq->tick = key_blockacq_lifetime; + spacq->count = 0; + } + } + { + struct mbuf *n, *mpolicy; struct sadb_msg *newmsg; - u_int len; - caddr_t p; + int off; /* create new sadb_msg to reply. */ - len = sizeof(struct sadb_msg) - + PFKEY_EXTLEN(mhp[SADB_X_EXT_POLICY]) - + PFKEY_EXTLEN(mhp[SADB_EXT_ADDRESS_SRC]) - + PFKEY_EXTLEN(mhp[SADB_EXT_ADDRESS_DST]); + n = key_gather_mbuf(m, mhp, 2, 4, SADB_EXT_RESERVED, + SADB_X_EXT_POLICY, SADB_EXT_ADDRESS_SRC, SADB_EXT_ADDRESS_DST); + if (!n) + return key_senderror(so, m, ENOBUFS); - KMALLOC(newmsg, struct sadb_msg *, len); - if (newmsg == NULL) { - printf("key_spdadd: No more memory.\n"); - msg0->sadb_msg_errno = ENOBUFS; - return NULL; + if (n->m_len < sizeof(*newmsg)) { + n = m_pullup(n, sizeof(*newmsg)); + if (!n) + return key_senderror(so, m, ENOBUFS); } - bzero((caddr_t)newmsg, len); - - bcopy((caddr_t)msg0, (caddr_t)newmsg, sizeof(*msg0)); + newmsg = mtod(n, struct sadb_msg *); newmsg->sadb_msg_errno = 0; - newmsg->sadb_msg_len = PFKEY_UNIT64(len); - p = (caddr_t)newmsg + sizeof(*msg0); + newmsg->sadb_msg_len = PFKEY_UNIT64(n->m_pkthdr.len); - p = key_setsadbext(p, mhp[SADB_X_EXT_POLICY]); - p = key_setsadbext(p, mhp[SADB_EXT_ADDRESS_SRC]); - p = key_setsadbext(p, mhp[SADB_EXT_ADDRESS_DST]); + off = 0; + mpolicy = m_pulldown(n, PFKEY_ALIGN8(sizeof(struct sadb_msg)), + sizeof(*xpl), &off); + if (mpolicy == NULL) { + /* n is already freed */ + return key_senderror(so, m, ENOBUFS); + } + xpl = (struct sadb_x_policy *)(mtod(mpolicy, caddr_t) + off); + if (xpl->sadb_x_policy_exttype != SADB_X_EXT_POLICY) { + m_freem(n); + return key_senderror(so, m, EINVAL); + } + xpl->sadb_x_policy_id = newsp->id; - return newmsg; + m_freem(m); + return key_sendup_mbuf(so, n, KEY_SENDUP_ALL); } } /* + * get new policy id. + * OUT: + * 0: failure. + * others: success. + */ +static u_int32_t +key_getnewspid() +{ + u_int32_t newid = 0; + int count = key_spi_trycnt; /* XXX */ + struct secpolicy *sp; + + /* when requesting to allocate spi ranged */ + while (count--) { + newid = (policy_id = (policy_id == ~0 ? 1 : ++policy_id)); + + if ((sp = key_getspbyid(newid)) == NULL) + break; + + key_freesp(sp); + } + + if (count == 0 || newid == 0) { +#ifdef IPSEC_DEBUG + printf("key_getnewspid: to allocate policy id is failed.\n"); +#endif + return 0; + } + + return newid; +} + +/* * SADB_SPDDELETE processing * receive * * from the user(?), and set SADB_SASTATE_DEAD, * and send, * * to the ikmpd. * policy(*) including direction of policy. * - * IN: mhp: pointer to the pointer to each header. - * OUT: other if success, return pointer to the message to send. - * 0 if fail. + * m will always be freed. */ -static struct sadb_msg * -key_spddelete(mhp) - caddr_t *mhp; +static int +key_spddelete(so, m, mhp) + struct socket *so; + struct mbuf *m; + const struct sadb_msghdr *mhp; { - struct sadb_msg *msg0; struct sadb_address *src0, *dst0; struct sadb_x_policy *xpl0; struct secpolicyindex spidx; struct secpolicy *sp; /* sanity check */ - if (mhp == NULL || mhp[0] == NULL) + if (so == NULL || m == NULL || mhp == NULL || mhp->msg == NULL) panic("key_spddelete: NULL pointer is passed.\n"); - msg0 = (struct sadb_msg *)mhp[0]; - - if (mhp[SADB_EXT_ADDRESS_SRC] == NULL - || mhp[SADB_EXT_ADDRESS_DST] == NULL - || mhp[SADB_X_EXT_POLICY] == NULL) { + if (mhp->ext[SADB_EXT_ADDRESS_SRC] == NULL || + mhp->ext[SADB_EXT_ADDRESS_DST] == NULL || + mhp->ext[SADB_X_EXT_POLICY] == NULL) { +#ifdef IPSEC_DEBUG printf("key_spddelete: invalid message is passed.\n"); - msg0->sadb_msg_errno = EINVAL; - return NULL; +#endif + return key_senderror(so, m, EINVAL); } + if (mhp->extlen[SADB_EXT_ADDRESS_SRC] < sizeof(struct sadb_address) || + mhp->extlen[SADB_EXT_ADDRESS_DST] < sizeof(struct sadb_address) || + mhp->extlen[SADB_X_EXT_POLICY] < sizeof(struct sadb_x_policy)) { +#ifdef IPSEC_DEBUG + printf("key_spddelete: invalid message is passed.\n"); +#endif + return key_senderror(so, m, EINVAL); + } - src0 = (struct sadb_address *)(mhp[SADB_EXT_ADDRESS_SRC]); - dst0 = (struct sadb_address *)(mhp[SADB_EXT_ADDRESS_DST]); - xpl0 = (struct sadb_x_policy *)mhp[SADB_X_EXT_POLICY]; + src0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_SRC]; + dst0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_DST]; + xpl0 = (struct sadb_x_policy *)mhp->ext[SADB_X_EXT_POLICY]; /* make secindex */ + /* XXX boundary check against sa_len */ KEY_SETSECSPIDX(xpl0->sadb_x_policy_dir, src0 + 1, dst0 + 1, src0->sadb_address_prefixlen, dst0->sadb_address_prefixlen, src0->sadb_address_proto, &spidx); /* checking the direciton. */ switch (xpl0->sadb_x_policy_dir) { case IPSEC_DIR_INBOUND: case IPSEC_DIR_OUTBOUND: break; default: +#ifdef IPSEC_DEBUG printf("key_spddelete: Invalid SP direction.\n"); - msg0->sadb_msg_errno = EINVAL; - return NULL; +#endif + return key_senderror(so, m, EINVAL); } /* Is there SP in SPD ? */ if ((sp = key_getsp(&spidx)) == NULL) { +#ifdef IPSEC_DEBUG printf("key_spddelete: no SP found.\n"); - msg0->sadb_msg_errno = ENOENT; - return NULL; +#endif + return key_senderror(so, m, EINVAL); } + /* save policy id to buffer to be returned. */ + xpl0->sadb_x_policy_id = sp->id; + sp->state = IPSEC_SPSTATE_DEAD; key_freesp(sp); { + struct mbuf *n; struct sadb_msg *newmsg; - u_int len; - caddr_t p; /* create new sadb_msg to reply. */ - len = sizeof(struct sadb_msg) - + PFKEY_EXTLEN(mhp[SADB_X_EXT_POLICY]) - + PFKEY_EXTLEN(mhp[SADB_EXT_ADDRESS_SRC]) - + PFKEY_EXTLEN(mhp[SADB_EXT_ADDRESS_DST]); + n = key_gather_mbuf(m, mhp, 1, 4, SADB_EXT_RESERVED, + SADB_X_EXT_POLICY, SADB_EXT_ADDRESS_SRC, SADB_EXT_ADDRESS_DST); + if (!n) + return key_senderror(so, m, ENOBUFS); - KMALLOC(newmsg, struct sadb_msg *, len); - if (newmsg == NULL) { - printf("key_spddelete: No more memory.\n"); - msg0->sadb_msg_errno = ENOBUFS; - return NULL; + newmsg = mtod(n, struct sadb_msg *); + newmsg->sadb_msg_errno = 0; + newmsg->sadb_msg_len = PFKEY_UNIT64(n->m_pkthdr.len); + + m_freem(m); + return key_sendup_mbuf(so, n, KEY_SENDUP_ALL); + } +} + +/* + * SADB_SPDDELETE2 processing + * receive + * + * from the user(?), and set SADB_SASTATE_DEAD, + * and send, + * + * to the ikmpd. + * policy(*) including direction of policy. + * + * m will always be freed. + */ +static int +key_spddelete2(so, m, mhp) + struct socket *so; + struct mbuf *m; + const struct sadb_msghdr *mhp; +{ + u_int32_t id; + struct secpolicy *sp; + + /* sanity check */ + if (so == NULL || m == NULL || mhp == NULL || mhp->msg == NULL) + panic("key_spddelete2: NULL pointer is passed.\n"); + + if (mhp->ext[SADB_X_EXT_POLICY] == NULL || + mhp->extlen[SADB_X_EXT_POLICY] < sizeof(struct sadb_x_policy)) { +#ifdef IPSEC_DEBUG + printf("key_spddelete2: invalid message is passed.\n"); +#endif + key_senderror(so, m, EINVAL); + return 0; } - bzero((caddr_t)newmsg, len); - bcopy((caddr_t)mhp[0], (caddr_t)newmsg, sizeof(*msg0)); - newmsg->sadb_msg_errno = 0; - newmsg->sadb_msg_len = PFKEY_UNIT64(len); - p = (caddr_t)newmsg + sizeof(*msg0); + id = ((struct sadb_x_policy *)mhp->ext[SADB_X_EXT_POLICY])->sadb_x_policy_id; - p = key_setsadbext(p, mhp[SADB_X_EXT_POLICY]); - p = key_setsadbext(p, mhp[SADB_EXT_ADDRESS_SRC]); - p = key_setsadbext(p, mhp[SADB_EXT_ADDRESS_DST]); + /* Is there SP in SPD ? */ + if ((sp = key_getspbyid(id)) == NULL) { +#ifdef IPSEC_DEBUG + printf("key_spddelete2: no SP found id:%u.\n", id); +#endif + key_senderror(so, m, EINVAL); + } - return newmsg; + sp->state = IPSEC_SPSTATE_DEAD; + key_freesp(sp); + + { + struct mbuf *n, *nn; + struct sadb_msg *newmsg; + int off, len; + + /* create new sadb_msg to reply. */ + len = PFKEY_ALIGN8(sizeof(struct sadb_msg)); + + if (len > MCLBYTES) + return key_senderror(so, m, ENOBUFS); + MGETHDR(n, M_DONTWAIT, MT_DATA); + if (n && len > MHLEN) { + MCLGET(n, M_DONTWAIT); + if ((n->m_flags & M_EXT) == 0) { + m_freem(n); + n = NULL; + } + } + if (!n) + return key_senderror(so, m, ENOBUFS); + + n->m_len = len; + n->m_next = NULL; + off = 0; + + m_copydata(m, 0, sizeof(struct sadb_msg), mtod(n, caddr_t) + off); + off += PFKEY_ALIGN8(sizeof(struct sadb_msg)); + +#ifdef DIAGNOSTIC + if (off != len) + panic("length inconsistency in key_spddelete2"); +#endif + + n->m_next = m_copym(m, mhp->extoff[SADB_X_EXT_POLICY], + mhp->extlen[SADB_X_EXT_POLICY], M_DONTWAIT); + if (!n->m_next) { + m_freem(n); + return key_senderror(so, m, ENOBUFS); + } + + n->m_pkthdr.len = 0; + for (nn = n; nn; nn = nn->m_next) + n->m_pkthdr.len += nn->m_len; + + newmsg = mtod(n, struct sadb_msg *); + newmsg->sadb_msg_errno = 0; + newmsg->sadb_msg_len = PFKEY_UNIT64(n->m_pkthdr.len); + + m_freem(m); + return key_sendup_mbuf(so, n, KEY_SENDUP_ALL); } } /* + * SADB_X_GET processing + * receive + * + * from the user(?), + * and send, + * + * to the ikmpd. + * policy(*) including direction of policy. + * + * m will always be freed. + */ +static int +key_spdget(so, m, mhp) + struct socket *so; + struct mbuf *m; + const struct sadb_msghdr *mhp; +{ + u_int32_t id; + struct secpolicy *sp; + struct mbuf *n; + + /* sanity check */ + if (so == NULL || m == NULL || mhp == NULL || mhp->msg == NULL) + panic("key_spdget: NULL pointer is passed.\n"); + + if (mhp->ext[SADB_X_EXT_POLICY] == NULL || + mhp->extlen[SADB_X_EXT_POLICY] < sizeof(struct sadb_x_policy)) { +#ifdef IPSEC_DEBUG + printf("key_spdget: invalid message is passed.\n"); +#endif + return key_senderror(so, m, EINVAL); + } + + id = ((struct sadb_x_policy *)mhp->ext[SADB_X_EXT_POLICY])->sadb_x_policy_id; + + /* Is there SP in SPD ? */ + if ((sp = key_getspbyid(id)) == NULL) { +#ifdef IPSEC_DEBUG + printf("key_spdget: no SP found id:%u.\n", id); +#endif + return key_senderror(so, m, ENOENT); + } + + n = key_setdumpsp(sp, SADB_X_SPDGET, 0, mhp->msg->sadb_msg_pid); + if (n != NULL) { + m_freem(m); + return key_sendup_mbuf(so, n, KEY_SENDUP_ONE); + } else + return key_senderror(so, m, ENOBUFS); +} + +/* + * SADB_X_SPDACQUIRE processing. + * Acquire policy and SA(s) for a *OUTBOUND* packet. + * send + * + * to KMD, and expect to receive + * with SADB_X_SPDACQUIRE if error occured, + * or + * + * with SADB_X_SPDUPDATE from KMD by PF_KEY. + * policy(*) is without policy requests. + * + * 0 : succeed + * others: error number + */ +int +key_spdacquire(sp) + struct secpolicy *sp; +{ + struct mbuf *result = NULL, *m; + struct secspacq *newspacq; + int error; + + /* sanity check */ + if (sp == NULL) + panic("key_spdacquire: NULL pointer is passed.\n"); + if (sp->req != NULL) + panic("key_spdacquire: called but there is request.\n"); + if (sp->policy != IPSEC_POLICY_IPSEC) + panic("key_spdacquire: policy mismathed. IPsec is expected.\n"); + + /* get a entry to check whether sent message or not. */ + if ((newspacq = key_getspacq(&sp->spidx)) != NULL) { + if (key_blockacq_count < newspacq->count) { + /* reset counter and do send message. */ + newspacq->count = 0; + } else { + /* increment counter and do nothing. */ + newspacq->count++; + return 0; + } + } else { + /* make new entry for blocking to send SADB_ACQUIRE. */ + if ((newspacq = key_newspacq(&sp->spidx)) == NULL) + return ENOBUFS; + + /* add to acqtree */ + LIST_INSERT_HEAD(&spacqtree, newspacq, chain); + } + + /* create new sadb_msg to reply. */ + m = key_setsadbmsg(SADB_X_SPDACQUIRE, 0, 0, 0, 0, 0); + if (!m) { + error = ENOBUFS; + goto fail; + } + result = m; + + result->m_pkthdr.len = 0; + for (m = result; m; m = m->m_next) + result->m_pkthdr.len += m->m_len; + + mtod(result, struct sadb_msg *)->sadb_msg_len = + PFKEY_UNIT64(result->m_pkthdr.len); + + return key_sendup_mbuf(NULL, m, KEY_SENDUP_REGISTERED); + +fail: + if (result) + m_freem(result); + return error; +} + +/* * SADB_SPDFLUSH processing * receive * * from the user, and free all entries in secpctree. * and send, * * to the user. * NOTE: what to do is only marking SADB_SASTATE_DEAD. * - * IN: mhp: pointer to the pointer to each header. - * OUT: other if success, return pointer to the message to send. - * 0 if fail. + * m will always be freed. */ -static struct sadb_msg * -key_spdflush(mhp) - caddr_t *mhp; +static int +key_spdflush(so, m, mhp) + struct socket *so; + struct mbuf *m; + const struct sadb_msghdr *mhp; { - struct sadb_msg *msg0; + struct sadb_msg *newmsg; struct secpolicy *sp; u_int dir; /* sanity check */ - if (mhp == NULL || mhp[0] == NULL) + if (so == NULL || m == NULL || mhp == NULL || mhp->msg == NULL) panic("key_spdflush: NULL pointer is passed.\n"); - msg0 = (struct sadb_msg *)mhp[0]; + if (m->m_len != PFKEY_ALIGN8(sizeof(struct sadb_msg))) + return key_senderror(so, m, EINVAL); for (dir = 0; dir < IPSEC_DIR_MAX; dir++) { - __LIST_FOREACH(sp, &sptree[dir], chain) { + LIST_FOREACH(sp, &sptree[dir], chain) { sp->state = IPSEC_SPSTATE_DEAD; } } - { - struct sadb_msg *newmsg; - u_int len; - - /* create new sadb_msg to reply. */ - len = sizeof(struct sadb_msg); - - KMALLOC(newmsg, struct sadb_msg *, len); - if (newmsg == NULL) { + if (sizeof(struct sadb_msg) > m->m_len + M_TRAILINGSPACE(m)) { +#ifdef IPSEC_DEBUG printf("key_spdflush: No more memory.\n"); - msg0->sadb_msg_errno = ENOBUFS; - return NULL; +#endif + return key_senderror(so, m, ENOBUFS); } - bzero((caddr_t)newmsg, len); - bcopy((caddr_t)mhp[0], (caddr_t)newmsg, sizeof(*msg0)); + if (m->m_next) + m_freem(m->m_next); + m->m_next = NULL; + m->m_pkthdr.len = m->m_len = PFKEY_ALIGN8(sizeof(struct sadb_msg)); + newmsg = mtod(m, struct sadb_msg *); newmsg->sadb_msg_errno = 0; - newmsg->sadb_msg_len = PFKEY_UNIT64(len); + newmsg->sadb_msg_len = PFKEY_UNIT64(m->m_pkthdr.len); - return(newmsg); - } + return key_sendup_mbuf(so, m, KEY_SENDUP_ALL); } /* * SADB_SPDDUMP processing * receive * * from the user, and dump all SP leaves * and send, * ..... * to the ikmpd. * - * IN: mhp: pointer to the pointer to each header. - * OUT: other if success, return pointer to the message to send. - * 0 if fail. + * m will always be freed. */ static int -key_spddump(mhp, so, target) - caddr_t *mhp; +key_spddump(so, m, mhp) struct socket *so; - int target; + struct mbuf *m; + const struct sadb_msghdr *mhp; { - struct sadb_msg *msg0; struct secpolicy *sp; - int len, cnt, cnt_sanity; - struct sadb_msg *newmsg; + int cnt; u_int dir; + struct mbuf *n; /* sanity check */ - if (mhp == NULL || mhp[0] == NULL) + if (so == NULL || m == NULL || mhp == NULL || mhp->msg == NULL) panic("key_spddump: NULL pointer is passed.\n"); - msg0 = (struct sadb_msg *)mhp[0]; - /* search SPD entry and get buffer size. */ - cnt = cnt_sanity = 0; + cnt = 0; for (dir = 0; dir < IPSEC_DIR_MAX; dir++) { - __LIST_FOREACH(sp, &sptree[dir], chain) { + LIST_FOREACH(sp, &sptree[dir], chain) { cnt++; } } if (cnt == 0) - return ENOENT; + return key_senderror(so, m, ENOENT); - newmsg = NULL; for (dir = 0; dir < IPSEC_DIR_MAX; dir++) { - __LIST_FOREACH(sp, &sptree[dir], chain) { - len = key_getspmsglen(sp); - - /* making buffer */ - KMALLOC(newmsg, struct sadb_msg *, len); - if (newmsg == NULL) { - printf("key_spddump: No more memory.\n"); - return ENOBUFS; - } - bzero((caddr_t)newmsg, len); - + LIST_FOREACH(sp, &sptree[dir], chain) { --cnt; - (void)key_setdumpsp(newmsg, sp, SADB_X_SPDDUMP, - cnt, msg0->sadb_msg_pid); + n = key_setdumpsp(sp, SADB_X_SPDDUMP, cnt, + mhp->msg->sadb_msg_pid); - key_sendup(so, newmsg, len, target); - KFREE(newmsg); - newmsg = NULL; + if (n) + key_sendup_mbuf(so, n, KEY_SENDUP_ONE); } } + m_freem(m); return 0; } -static u_int -key_setdumpsp(newmsg, sp, type, seq, pid) - struct sadb_msg *newmsg; +static struct mbuf * +key_setdumpsp(sp, type, seq, pid) struct secpolicy *sp; u_int8_t type; u_int32_t seq, pid; { - u_int tlen; - caddr_t p; + struct mbuf *result = NULL, *m; - tlen = key_getspmsglen(sp); + m = key_setsadbmsg(type, 0, SADB_SATYPE_UNSPEC, seq, pid, sp->refcnt); + if (!m) + goto fail; + result = m; - p = key_setsadbmsg((caddr_t)newmsg, type, tlen, - SADB_SATYPE_UNSPEC, seq, pid, - IPSEC_MODE_ANY, sp->refcnt); + m = key_setsadbaddr(SADB_EXT_ADDRESS_SRC, + (struct sockaddr *)&sp->spidx.src, sp->spidx.prefs, + sp->spidx.ul_proto); + if (!m) + goto fail; + m_cat(result, m); - p = key_setsadbaddr(p, - SADB_EXT_ADDRESS_SRC, - (struct sockaddr *)&sp->spidx.src, - sp->spidx.prefs, - sp->spidx.ul_proto); - p = key_setsadbaddr(p, - SADB_EXT_ADDRESS_DST, - (struct sockaddr *)&sp->spidx.dst, - sp->spidx.prefd, - sp->spidx.ul_proto); + m = key_setsadbaddr(SADB_EXT_ADDRESS_DST, + (struct sockaddr *)&sp->spidx.dst, sp->spidx.prefd, + sp->spidx.ul_proto); + if (!m) + goto fail; + m_cat(result, m); - { - struct sadb_x_policy *tmp; + m = key_sp2msg(sp); + if (!m) + goto fail; + m_cat(result, m); - if ((tmp = key_sp2msg(sp)) == NULL) { - printf("key_setdumpsp: No more memory.\n"); - return ENOBUFS; + if ((result->m_flags & M_PKTHDR) == 0) + goto fail; + + if (result->m_len < sizeof(struct sadb_msg)) { + result = m_pullup(result, sizeof(struct sadb_msg)); + if (result == NULL) + goto fail; } - /* validity check */ - if (key_getspreqmsglen(sp) != PFKEY_UNUNIT64(tmp->sadb_x_policy_len)) - panic("key_setdumpsp: length mismatch." - "sp:%d msg:%d\n", - key_getspreqmsglen(sp), - PFKEY_UNUNIT64(tmp->sadb_x_policy_len)); + result->m_pkthdr.len = 0; + for (m = result; m; m = m->m_next) + result->m_pkthdr.len += m->m_len; - bcopy(tmp, p, PFKEY_UNUNIT64(tmp->sadb_x_policy_len)); - KFREE(tmp); - } + mtod(result, struct sadb_msg *)->sadb_msg_len = + PFKEY_UNIT64(result->m_pkthdr.len); - return tlen; -} + return result; -/* get sadb message length for a SP. */ -static u_int -key_getspmsglen(sp) - struct secpolicy *sp; -{ - u_int tlen; - - /* sanity check */ - if (sp == NULL) - panic("key_getspmsglen: NULL pointer is passed.\n"); - - tlen = (sizeof(struct sadb_msg) - + sizeof(struct sadb_address) - + PFKEY_ALIGN8(_SALENBYAF(sp->spidx.src.ss_family)) - + sizeof(struct sadb_address) - + PFKEY_ALIGN8(_SALENBYAF(sp->spidx.dst.ss_family))); - - tlen += key_getspreqmsglen(sp); - - return tlen; +fail: + m_freem(result); + return NULL; } /* * get PFKEY message length for security policy and request. */ static u_int key_getspreqmsglen(sp) struct secpolicy *sp; { u_int tlen; tlen = sizeof(struct sadb_x_policy); /* if is the policy for ipsec ? */ if (sp->policy != IPSEC_POLICY_IPSEC) return tlen; /* get length of ipsec requests */ { struct ipsecrequest *isr; int len; for (isr = sp->req; isr != NULL; isr = isr->next) { len = sizeof(struct sadb_x_ipsecrequest) + isr->saidx.src.ss_len + isr->saidx.dst.ss_len; tlen += PFKEY_ALIGN8(len); } } return tlen; } /* %%% SAD management */ /* * allocating a memory for new SA head, and copy from the values of mhp. * OUT: NULL : failure due to the lack of memory. * others : pointer to new SA head. */ static struct secashead * key_newsah(saidx) struct secasindex *saidx; { struct secashead *newsah; - u_int stateidx; /* sanity check */ if (saidx == NULL) panic("key_newsaidx: NULL pointer is passed.\n"); - KMALLOC(newsah, struct secashead *, sizeof(struct secashead)); - if (newsah == NULL) { + newsah = keydb_newsecashead(); + if (newsah == NULL) return NULL; - } - bzero((caddr_t)newsah, sizeof(struct secashead)); bcopy(saidx, &newsah->saidx, sizeof(newsah->saidx)); - for (stateidx = 0; - stateidx < _ARRAYLEN(saorder_state_any); - stateidx++) { - LIST_INIT(&newsah->savtree[saorder_state_any[stateidx]]); - } - /* add to saidxtree */ newsah->state = SADB_SASTATE_MATURE; LIST_INSERT_HEAD(&sahtree, newsah, chain); return(newsah); } /* * delete SA index and all SA registerd. */ static void key_delsah(sah) struct secashead *sah; { struct secasvar *sav, *nextsav; u_int stateidx, state; int s; + int zombie = 0; /* sanity check */ if (sah == NULL) panic("key_delsah: NULL pointer is passed.\n"); s = splnet(); /*called from softclock()*/ - /* remove from tree of SA index */ - if (__LIST_CHAINED(sah)) - LIST_REMOVE(sah, chain); - /* searching all SA registerd in the secindex. */ for (stateidx = 0; stateidx < _ARRAYLEN(saorder_state_any); stateidx++) { state = saorder_state_any[stateidx]; for (sav = (struct secasvar *)LIST_FIRST(&sah->savtree[state]); sav != NULL; sav = nextsav) { nextsav = LIST_NEXT(sav, chain); + if (sav->refcnt > 0) { + /* give up to delete this sa */ + zombie++; + continue; + } + /* sanity check */ KEY_CHKSASTATE(state, sav->state, "key_delsah"); + key_freesav(sav); + /* remove back pointer */ sav->sah = NULL; - - if (sav->refcnt < 0) { - printf("key_delsah: why refcnt < 0 ?, " - "sav->refcnt=%d\n", - sav->refcnt); - } - key_freesav(sav); sav = NULL; } } + /* don't delete sah only if there are savs. */ + if (zombie) { + splx(s); + return; + } + if (sah->sa_route.ro_rt) { RTFREE(sah->sa_route.ro_rt); sah->sa_route.ro_rt = (struct rtentry *)NULL; } + /* remove from tree of SA index */ + if (__LIST_CHAINED(sah)) + LIST_REMOVE(sah, chain); + KFREE(sah); splx(s); return; } /* * allocating a new SA with LARVAL state. key_add() and key_getspi() call, * and copy the values of mhp into new buffer. * When SAD message type is GETSPI: * to set sequence number from acq_seq++, * to set zero to SPI. * not to call key_setsava(). * OUT: NULL : fail * others : pointer to new secasvar. + * + * does not modify mbuf. does not free mbuf on error. */ static struct secasvar * -key_newsav(mhp, sah) - caddr_t *mhp; +key_newsav(m, mhp, sah, errp) + struct mbuf *m; + const struct sadb_msghdr *mhp; struct secashead *sah; + int *errp; { struct secasvar *newsav; - struct sadb_msg *msg0; + const struct sadb_sa *xsa; /* sanity check */ - if (mhp == NULL || mhp[0] == NULL || sah == NULL) + if (m == NULL || mhp == NULL || mhp->msg == NULL || sah == NULL) panic("key_newsa: NULL pointer is passed.\n"); - msg0 = (struct sadb_msg *)mhp[0]; - KMALLOC(newsav, struct secasvar *, sizeof(struct secasvar)); if (newsav == NULL) { +#ifdef IPSEC_DEBUG printf("key_newsa: No more memory.\n"); - msg0->sadb_msg_errno = ENOBUFS; +#endif + *errp = ENOBUFS; return NULL; } bzero((caddr_t)newsav, sizeof(struct secasvar)); - switch (msg0->sadb_msg_type) { + switch (mhp->msg->sadb_msg_type) { case SADB_GETSPI: newsav->spi = 0; +#ifdef IPSEC_DOSEQCHECK /* sync sequence number */ - if (msg0->sadb_msg_seq == 0) + if (mhp->msg->sadb_msg_seq == 0) newsav->seq = (acq_seq = (acq_seq == ~0 ? 1 : ++acq_seq)); else - newsav->seq = msg0->sadb_msg_seq; +#endif + newsav->seq = mhp->msg->sadb_msg_seq; break; case SADB_ADD: /* sanity check */ - if (mhp[SADB_EXT_SA] == NULL) { + if (mhp->ext[SADB_EXT_SA] == NULL) { KFREE(newsav); +#ifdef IPSEC_DEBUG printf("key_newsa: invalid message is passed.\n"); - msg0->sadb_msg_errno = EINVAL; +#endif + *errp = EINVAL; return NULL; } - newsav->spi = ((struct sadb_sa *)mhp[SADB_EXT_SA])->sadb_sa_spi; - newsav->seq = msg0->sadb_msg_seq; + xsa = (const struct sadb_sa *)mhp->ext[SADB_EXT_SA]; + newsav->spi = xsa->sadb_sa_spi; + newsav->seq = mhp->msg->sadb_msg_seq; break; default: KFREE(newsav); - msg0->sadb_msg_errno = EINVAL; + *errp = EINVAL; return NULL; } /* copy sav values */ - if (msg0->sadb_msg_type != SADB_GETSPI && key_setsaval(newsav, mhp)) { - KFREE(newsav); - /* msg0->sadb_msg_errno is set at key_setsaval. */ - return NULL; + if (mhp->msg->sadb_msg_type != SADB_GETSPI) { + *errp = key_setsaval(newsav, m, mhp); + if (*errp) { + KFREE(newsav); + return NULL; + } } /* reset tick */ newsav->tick = 0; - newsav->pid = msg0->sadb_msg_pid; + newsav->pid = mhp->msg->sadb_msg_pid; /* add to satree */ newsav->sah = sah; newsav->refcnt = 1; newsav->state = SADB_SASTATE_LARVAL; - LIST_INSERT_HEAD(&sah->savtree[SADB_SASTATE_LARVAL], newsav, chain); + LIST_INSERT_TAIL(&sah->savtree[SADB_SASTATE_LARVAL], newsav, + secasvar, chain); return newsav; } /* * free() SA variable entry. */ static void key_delsav(sav) struct secasvar *sav; { /* sanity check */ if (sav == NULL) panic("key_delsav: NULL pointer is passed.\n"); - if (sav->refcnt > 0) return; /* can't free */ + if (sav->refcnt > 0) + return; /* can't free */ /* remove from SA header */ if (__LIST_CHAINED(sav)) LIST_REMOVE(sav, chain); if (sav->key_auth != NULL) KFREE(sav->key_auth); if (sav->key_enc != NULL) KFREE(sav->key_enc); - if (sav->replay != NULL) { - if (sav->replay->bitmap != NULL) - KFREE(sav->replay->bitmap); - KFREE(sav->replay); - } + if (sav->replay != NULL) + keydb_delsecreplay(sav->replay); if (sav->lft_c != NULL) KFREE(sav->lft_c); if (sav->lft_h != NULL) KFREE(sav->lft_h); if (sav->lft_s != NULL) KFREE(sav->lft_s); if (sav->iv != NULL) KFREE(sav->iv); #if notyet if (sav->misc1 != NULL) KFREE(sav->misc1); if (sav->misc2 != NULL) KFREE(sav->misc2); if (sav->misc3 != NULL) KFREE(sav->misc3); #endif - sav->sah = NULL; - /* XXX for making sure. See key_checkrequest(), - * Refcnt may be suspicious. */ - KFREE(sav); return; } /* * search SAD. * OUT: * NULL : not found * others : found, pointer to a SA. */ static struct secashead * key_getsah(saidx) struct secasindex *saidx; { struct secashead *sah; - __LIST_FOREACH(sah, &sahtree, chain) { + LIST_FOREACH(sah, &sahtree, chain) { if (sah->state == SADB_SASTATE_DEAD) continue; if (key_cmpsaidx_exactly(&sah->saidx, saidx)) return(sah); } return NULL; } /* * check not to be duplicated SPI. * NOTE: this function is too slow due to searching all SAD. * OUT: * NULL : not found * others : found, pointer to a SA. */ static struct secasvar * key_checkspidup(saidx, spi) struct secasindex *saidx; u_int32_t spi; { struct secashead *sah; struct secasvar *sav; /* check address family */ - if (saidx->src.ss_family != saidx->src.ss_family) { + if (saidx->src.ss_family != saidx->dst.ss_family) { +#ifdef IPSEC_DEBUG printf("key_checkspidup: address family mismatched.\n"); +#endif return NULL; } /* check all SAD */ - __LIST_FOREACH(sah, &sahtree, chain) { - if (!key_ismyaddr(sah->saidx.dst.ss_family, - _INADDRBYSA(&sah->saidx.dst))) + LIST_FOREACH(sah, &sahtree, chain) { + if (!key_ismyaddr((struct sockaddr *)&sah->saidx.dst)) continue; sav = key_getsavbyspi(sah, spi); if (sav != NULL) return sav; } return NULL; } /* * search SAD litmited alive SA, protocol, SPI. * OUT: * NULL : not found * others : found, pointer to a SA. */ static struct secasvar * key_getsavbyspi(sah, spi) struct secashead *sah; u_int32_t spi; { struct secasvar *sav; u_int stateidx, state; /* search all status */ for (stateidx = 0; stateidx < _ARRAYLEN(saorder_state_alive); stateidx++) { state = saorder_state_alive[stateidx]; - __LIST_FOREACH(sav, &sah->savtree[state], chain) { + LIST_FOREACH(sav, &sah->savtree[state], chain) { /* sanity check */ if (sav->state != state) { +#ifdef IPSEC_DEBUG printf("key_getsavbyspi: " "invalid sav->state " "(queue: %d SA: %d)\n", state, sav->state); +#endif continue; } if (sav->spi == spi) return sav; } } return NULL; } /* * copy SA values from PF_KEY message except *SPI, SEQ, PID, STATE and TYPE*. * You must update these if need. * OUT: 0: success. - * 1: failure. set errno to (mhp[0])->sadb_msg_errno. + * !0: failure. + * + * does not modify mbuf. does not free mbuf on error. */ static int -key_setsaval(sav, mhp) +key_setsaval(sav, m, mhp) struct secasvar *sav; - caddr_t *mhp; + struct mbuf *m; + const struct sadb_msghdr *mhp; { - struct sadb_msg *msg0; +#ifdef IPSEC_ESP + const struct esp_algorithm *algo; +#endif int error = 0; /* sanity check */ - if (mhp == NULL || mhp[0] == NULL) + if (m == NULL || mhp == NULL || mhp->msg == NULL) panic("key_setsaval: NULL pointer is passed.\n"); - msg0 = (struct sadb_msg *)mhp[0]; - /* initialization */ sav->replay = NULL; sav->key_auth = NULL; sav->key_enc = NULL; sav->iv = NULL; sav->lft_c = NULL; sav->lft_h = NULL; sav->lft_s = NULL; +#if notyet + sav->misc1 = NULL; + sav->misc2 = NULL; + sav->misc3 = NULL; +#endif /* SA */ - if (mhp[SADB_EXT_SA] != NULL) { - struct sadb_sa *sa0 = (struct sadb_sa *)mhp[SADB_EXT_SA]; + if (mhp->ext[SADB_EXT_SA] != NULL) { + const struct sadb_sa *sa0; + sa0 = (const struct sadb_sa *)mhp->ext[SADB_EXT_SA]; + if (mhp->extlen[SADB_EXT_SA] < sizeof(*sa0)) { + error = EINVAL; + goto fail; + } + sav->alg_auth = sa0->sadb_sa_auth; sav->alg_enc = sa0->sadb_sa_encrypt; sav->flags = sa0->sadb_sa_flags; /* replay window */ if ((sa0->sadb_sa_flags & SADB_X_EXT_OLD) == 0) { - KMALLOC(sav->replay, struct secreplay *, - sizeof(struct secreplay)); + sav->replay = keydb_newsecreplay(sa0->sadb_sa_replay); if (sav->replay == NULL) { +#ifdef IPSEC_DEBUG printf("key_setsaval: No more memory.\n"); +#endif error = ENOBUFS; - goto err; + goto fail; } - bzero(sav->replay, sizeof(struct secreplay)); - - if ((sav->replay->wsize = sa0->sadb_sa_replay) != 0) { - KMALLOC(sav->replay->bitmap, caddr_t, - sav->replay->wsize); - if (sav->replay->bitmap == NULL) { - printf("key_setsaval: " - "No more memory.\n"); - error = ENOBUFS; - goto err; - } - bzero(sav->replay->bitmap, sa0->sadb_sa_replay); - } } } /* Authentication keys */ - if (mhp[SADB_EXT_KEY_AUTH] != NULL) { - struct sadb_key *key0; - u_int len; + if (mhp->ext[SADB_EXT_KEY_AUTH] != NULL) { + const struct sadb_key *key0; + int len; - key0 = (struct sadb_key *)mhp[SADB_EXT_KEY_AUTH]; - len = PFKEY_UNUNIT64(key0->sadb_key_len); + key0 = (const struct sadb_key *)mhp->ext[SADB_EXT_KEY_AUTH]; + len = mhp->extlen[SADB_EXT_KEY_AUTH]; error = 0; - if (len < sizeof(struct sadb_key)) + if (len < sizeof(*key0)) { error = EINVAL; - switch (msg0->sadb_msg_satype) { + goto fail; + } + switch (mhp->msg->sadb_msg_satype) { case SADB_SATYPE_AH: case SADB_SATYPE_ESP: - if (len == sizeof(struct sadb_key) - && sav->alg_auth != SADB_AALG_NULL) { + if (len == PFKEY_ALIGN8(sizeof(struct sadb_key)) && + sav->alg_auth != SADB_AALG_NULL) error = EINVAL; - } break; case SADB_X_SATYPE_IPCOMP: - error = EINVAL; - break; default: error = EINVAL; break; } if (error) { +#ifdef IPSEC_DEBUG printf("key_setsaval: invalid key_auth values.\n"); - goto err; +#endif + goto fail; } - KEY_NEWBUF(sav->key_auth, struct sadb_key *, key0, len); + sav->key_auth = (struct sadb_key *)key_newbuf(key0, len); if (sav->key_auth == NULL) { +#ifdef IPSEC_DEBUG printf("key_setsaval: No more memory.\n"); +#endif error = ENOBUFS; - goto err; + goto fail; } - - /* make length shift up for kernel*/ - sav->key_auth->sadb_key_len = len; } /* Encryption key */ - if (mhp[SADB_EXT_KEY_ENCRYPT] != NULL) { - struct sadb_key *key0; - u_int len; + if (mhp->ext[SADB_EXT_KEY_ENCRYPT] != NULL) { + const struct sadb_key *key0; + int len; - key0 = (struct sadb_key *)mhp[SADB_EXT_KEY_ENCRYPT]; - len = PFKEY_UNUNIT64(key0->sadb_key_len); + key0 = (const struct sadb_key *)mhp->ext[SADB_EXT_KEY_ENCRYPT]; + len = mhp->extlen[SADB_EXT_KEY_ENCRYPT]; error = 0; - if (len < sizeof(struct sadb_key)) + if (len < sizeof(*key0)) { error = EINVAL; - switch (msg0->sadb_msg_satype) { + goto fail; + } + switch (mhp->msg->sadb_msg_satype) { case SADB_SATYPE_ESP: - if (len == sizeof(struct sadb_key) - && sav->alg_enc != SADB_EALG_NULL) { + if (len == PFKEY_ALIGN8(sizeof(struct sadb_key)) && + sav->alg_enc != SADB_EALG_NULL) error = EINVAL; - } break; case SADB_SATYPE_AH: - error = EINVAL; - break; case SADB_X_SATYPE_IPCOMP: - break; - default: error = EINVAL; break; } if (error) { +#ifdef IPSEC_DEBUG printf("key_setsatval: invalid key_enc value.\n"); - goto err; +#endif + goto fail; } - KEY_NEWBUF(sav->key_enc, struct sadb_key *, key0, len); + sav->key_enc = (struct sadb_key *)key_newbuf(key0, len); if (sav->key_enc == NULL) { +#ifdef IPSEC_DEBUG printf("key_setsaval: No more memory.\n"); +#endif error = ENOBUFS; - goto err; + goto fail; } - - /* make length shift up for kernel*/ - sav->key_enc->sadb_key_len = len; } /* set iv */ sav->ivlen = 0; - switch (msg0->sadb_msg_satype) { + switch (mhp->msg->sadb_msg_satype) { case SADB_SATYPE_ESP: #ifdef IPSEC_ESP - { - struct esp_algorithm *algo; - algo = &esp_algorithms[sav->alg_enc]; if (algo && algo->ivlen) sav->ivlen = (*algo->ivlen)(sav); + if (sav->ivlen == 0) + break; KMALLOC(sav->iv, caddr_t, sav->ivlen); if (sav->iv == 0) { +#ifdef IPSEC_DEBUG printf("key_setsaval: No more memory.\n"); +#endif error = ENOBUFS; - goto err; + goto fail; } /* initialize ? */ break; - } #else break; #endif case SADB_SATYPE_AH: +#if 1 /*nonstandard*/ + case SADB_X_SATYPE_IPCOMP: +#endif break; default: +#ifdef IPSEC_DEBUG printf("key_setsaval: invalid SA type.\n"); +#endif error = EINVAL; - goto err; + goto fail; } /* reset tick */ sav->tick = 0; /* make lifetime for CURRENT */ { struct timeval tv; KMALLOC(sav->lft_c, struct sadb_lifetime *, - sizeof(struct sadb_lifetime)); + sizeof(struct sadb_lifetime)); if (sav->lft_c == NULL) { +#ifdef IPSEC_DEBUG printf("key_setsaval: No more memory.\n"); +#endif error = ENOBUFS; - goto err; + goto fail; } microtime(&tv); sav->lft_c->sadb_lifetime_len = - PFKEY_UNIT64(sizeof(struct sadb_lifetime)); + PFKEY_UNIT64(sizeof(struct sadb_lifetime)); sav->lft_c->sadb_lifetime_exttype = SADB_EXT_LIFETIME_CURRENT; sav->lft_c->sadb_lifetime_allocations = 0; sav->lft_c->sadb_lifetime_bytes = 0; sav->lft_c->sadb_lifetime_addtime = tv.tv_sec; sav->lft_c->sadb_lifetime_usetime = 0; } /* lifetimes for HARD and SOFT */ { - struct sadb_lifetime *lft0; + const struct sadb_lifetime *lft0; - lft0 = (struct sadb_lifetime *)mhp[SADB_EXT_LIFETIME_HARD]; + lft0 = (struct sadb_lifetime *)mhp->ext[SADB_EXT_LIFETIME_HARD]; if (lft0 != NULL) { - KEY_NEWBUF(sav->lft_h, struct sadb_lifetime *, - lft0, sizeof(*lft0)); + if (mhp->extlen[SADB_EXT_LIFETIME_HARD] < sizeof(*lft0)) { + error = EINVAL; + goto fail; + } + sav->lft_h = (struct sadb_lifetime *)key_newbuf(lft0, + sizeof(*lft0)); if (sav->lft_h == NULL) { +#ifdef IPSEC_DEBUG printf("key_setsaval: No more memory.\n"); +#endif error = ENOBUFS; - goto err; + goto fail; } /* to be initialize ? */ } - lft0 = (struct sadb_lifetime *)mhp[SADB_EXT_LIFETIME_SOFT]; + lft0 = (struct sadb_lifetime *)mhp->ext[SADB_EXT_LIFETIME_SOFT]; if (lft0 != NULL) { - KEY_NEWBUF(sav->lft_s, struct sadb_lifetime *, - lft0, sizeof(*lft0)); + if (mhp->extlen[SADB_EXT_LIFETIME_SOFT] < sizeof(*lft0)) { + error = EINVAL; + goto fail; + } + sav->lft_s = (struct sadb_lifetime *)key_newbuf(lft0, + sizeof(*lft0)); if (sav->lft_s == NULL) { +#ifdef IPSEC_DEBUG printf("key_setsaval: No more memory.\n"); +#endif error = ENOBUFS; - goto err; + goto fail; } /* to be initialize ? */ } } - msg0->sadb_msg_errno = 0; +#if notyet + /* pre-processing for DES */ + switch (sav->alg_enc) { + case SADB_EALG_DESCBC: + if (des_key_sched((C_Block *)_KEYBUF(sav->key_enc), + (des_key_schedule)sav->misc1) != 0) { +#ifdef IPSEC_DEBUG + printf("key_setsaval: error des_key_sched.\n"); +#endif + sav->misc1 = NULL; + /* THROUGH */ + } + break; + case SADB_EALG_3DESCBC: + if (des_key_sched((C_Block *)_KEYBUF(sav->key_enc), + (des_key_schedule)sav->misc1) != 0 + || des_key_sched((C_Block *)(_KEYBUF(sav->key_enc) + 8), + (des_key_schedule)sav->misc2) != 0 + || des_key_sched((C_Block *)(_KEYBUF(sav->key_enc) + 16), + (des_key_schedule)sav->misc3) != 0) { +#ifdef IPSEC_DEBUG + printf("key_setsaval: error des_key_sched.\n"); +#endif + sav->misc1 = NULL; + sav->misc2 = NULL; + sav->misc3 = NULL; + /* THROUGH */ + } + } +#endif + return 0; - err: + fail: /* initialization */ - if (sav->replay != NULL) { - if (sav->replay->bitmap != NULL) - KFREE(sav->replay->bitmap); - KFREE(sav->replay); - } + if (sav->replay != NULL) + keydb_delsecreplay(sav->replay); if (sav->key_auth != NULL) KFREE(sav->key_auth); if (sav->key_enc != NULL) KFREE(sav->key_enc); if (sav->iv != NULL) KFREE(sav->iv); if (sav->lft_c != NULL) KFREE(sav->lft_c); if (sav->lft_h != NULL) KFREE(sav->lft_h); if (sav->lft_s != NULL) KFREE(sav->lft_s); +#if notyet + if (sav->misc1 != NULL) + KFREE(sav->misc1); + if (sav->misc2 != NULL) + KFREE(sav->misc2); + if (sav->misc3 != NULL) + KFREE(sav->misc3); +#endif - msg0->sadb_msg_errno = error; - return 1; + return error; } /* - * get message buffer length. - */ -static u_int -key_getmsglen(sav) - struct secasvar *sav; -{ - int len = sizeof(struct sadb_msg); - - len += sizeof(struct sadb_sa); - len += (sizeof(struct sadb_address) - + PFKEY_ALIGN8(_SALENBYAF(sav->sah->saidx.src.ss_family))); - len += (sizeof(struct sadb_address) - + PFKEY_ALIGN8(_SALENBYAF(sav->sah->saidx.dst.ss_family))); - - if (sav->key_auth != NULL) - len += sav->key_auth->sadb_key_len; - if (sav->key_enc != NULL) - len += sav->key_enc->sadb_key_len; - - if (sav->lft_c != NULL) - len += sizeof(struct sadb_lifetime); - if (sav->lft_h != NULL) - len += sizeof(struct sadb_lifetime); - if (sav->lft_s != NULL) - len += sizeof(struct sadb_lifetime); - - return len; -} - -/* * validation with a secasvar entry, and set SADB_SATYPE_MATURE. * OUT: 0: valid * other: errno */ static int key_mature(sav) struct secasvar *sav; { int mature; int checkmask = 0; /* 2^0: ealg 2^1: aalg 2^2: calg */ int mustmask = 0; /* 2^0: ealg 2^1: aalg 2^2: calg */ mature = 0; /* check SPI value */ if (ntohl(sav->spi) >= 0 && ntohl(sav->spi) <= 255) { +#ifdef IPSEC_DEBUG printf("key_mature: illegal range of SPI %d.\n", sav->spi); +#endif return EINVAL; } /* check satype */ switch (sav->sah->saidx.proto) { case IPPROTO_ESP: /* check flags */ if ((sav->flags & SADB_X_EXT_OLD) && (sav->flags & SADB_X_EXT_DERIV)) { +#ifdef IPSEC_DEBUG printf("key_mature: " "invalid flag (derived) given to old-esp.\n"); +#endif return EINVAL; } checkmask = 3; mustmask = 1; break; case IPPROTO_AH: /* check flags */ if (sav->flags & SADB_X_EXT_DERIV) { +#ifdef IPSEC_DEBUG printf("key_mature: " "invalid flag (derived) given to AH SA.\n"); +#endif return EINVAL; } if (sav->alg_enc != SADB_EALG_NONE) { +#ifdef IPSEC_DEBUG printf("key_mature: " "protocol and algorithm mismated.\n"); +#endif return(EINVAL); } checkmask = 2; mustmask = 2; break; +#if 1 /*nonstandard*/ + case IPPROTO_IPCOMP: + if (sav->alg_auth != SADB_AALG_NONE) { +#ifdef IPSEC_DEBUG + printf("key_mature: " + "protocol and algorithm mismated.\n"); +#endif + return(EINVAL); + } + if ((sav->flags & SADB_X_EXT_RAWCPI) == 0 + && ntohl(sav->spi) >= 0x10000) { +#ifdef IPSEC_DEBUG + printf("key_mature: invalid cpi for IPComp.\n"); +#endif + return(EINVAL); + } + checkmask = 4; + mustmask = 4; + break; +#endif default: +#ifdef IPSEC_DEBUG printf("key_mature: Invalid satype.\n"); +#endif return EPROTONOSUPPORT; } /* check authentication algorithm */ if ((checkmask & 2) != 0) { struct ah_algorithm *algo; int keylen; /* XXX: should use algorithm map to check. */ switch (sav->alg_auth) { case SADB_AALG_NONE: case SADB_AALG_MD5HMAC: case SADB_AALG_SHA1HMAC: case SADB_AALG_MD5: case SADB_AALG_SHA: case SADB_AALG_NULL: break; default: +#ifdef IPSEC_DEBUG printf("key_mature: " "unknown authentication algorithm.\n"); +#endif return EINVAL; } /* algorithm-dependent check */ algo = &ah_algorithms[sav->alg_auth]; if (sav->key_auth) keylen = sav->key_auth->sadb_key_bits; else keylen = 0; if (keylen < algo->keymin || algo->keymax < keylen) { +#ifdef IPSEC_DEBUG printf("key_mature: invalid AH key length %d " "(%d-%d allowed)\n", keylen, algo->keymin, algo->keymax); +#endif return EINVAL; } if (algo->mature) { if ((*algo->mature)(sav)) { /* message generated in per-algorithm function*/ return EINVAL; } else mature = SADB_SATYPE_AH; } - if ((mustmask & 2) != 0 && mature != SADB_SATYPE_AH) + if ((mustmask & 2) != 0 && mature != SADB_SATYPE_AH) { +#ifdef IPSEC_DEBUG + printf("key_mature: no satisfy algorithm for AH\n"); +#endif return EINVAL; + } } /* check encryption algorithm */ if ((checkmask & 1) != 0) { #ifdef IPSEC_ESP struct esp_algorithm *algo; int keylen; switch (sav->alg_enc) { case SADB_EALG_NONE: case SADB_EALG_DESCBC: case SADB_EALG_3DESCBC: case SADB_EALG_NULL: case SADB_EALG_BLOWFISHCBC: case SADB_EALG_CAST128CBC: case SADB_EALG_RC5CBC: break; default: +#ifdef IPSEC_DEBUG printf("key_mature: unknown encryption algorithm.\n"); - return(EINVAL); +#endif + return EINVAL; } /* algorithm-dependent check */ algo = &esp_algorithms[sav->alg_enc]; if (sav->key_enc) keylen = sav->key_enc->sadb_key_bits; else keylen = 0; if (keylen < algo->keymin || algo->keymax < keylen) { +#ifdef IPSEC_DEBUG printf("key_mature: invalid ESP key length %d " "(%d-%d allowed)\n", keylen, algo->keymin, algo->keymax); +#endif return EINVAL; } if (algo->mature) { if ((*algo->mature)(sav)) { /* message generated in per-algorithm function*/ return EINVAL; } else mature = SADB_SATYPE_ESP; } - if ((mustmask & 1) != 0 && mature != SADB_SATYPE_ESP) + if ((mustmask & 1) != 0 && mature != SADB_SATYPE_ESP) { +#ifdef IPSEC_DEBUG + printf("key_mature: no satisfy algorithm for ESP\n"); +#endif return EINVAL; -#else + } +#else /*IPSEC_ESP*/ +#ifdef IPSEC_DEBUG printf("key_mature: ESP not supported in this configuration\n"); +#endif return EINVAL; #endif } + /* check compression algorithm */ + if ((checkmask & 4) != 0) { + struct ipcomp_algorithm *algo; + + switch (sav->alg_enc) { + case SADB_X_CALG_NONE: + case SADB_X_CALG_OUI: + case SADB_X_CALG_DEFLATE: + case SADB_X_CALG_LZS: + break; + default: +#ifdef IPSEC_DEBUG + printf("key_mature: unknown compression algorithm.\n"); +#endif + return EINVAL; + } + + /* algorithm-dependent check */ + algo = &ipcomp_algorithms[sav->alg_enc]; + + if (!(algo->compress && algo->decompress)) { +#ifdef IPSEC_DEBUG + printf("key_mature: " + "unsupported compression algorithm.\n"); +#endif + return EINVAL; + } + } + key_sa_chgstate(sav, SADB_SASTATE_MATURE); return 0; } /* * subroutine for SADB_GET and SADB_DUMP. - * the buf must be allocated sufficent space. */ -static u_int -key_setdumpsa(newmsg, sav, type, satype, seq, pid) - struct sadb_msg *newmsg; +static struct mbuf * +key_setdumpsa(sav, type, satype, seq, pid) struct secasvar *sav; u_int8_t type, satype; u_int32_t seq, pid; { - u_int tlen; - caddr_t p; + struct mbuf *result = NULL, *tres = NULL, *m; + int l = 0; int i; + void *p; + int dumporder[] = { + SADB_EXT_SA, SADB_X_EXT_SA2, + SADB_EXT_LIFETIME_HARD, SADB_EXT_LIFETIME_SOFT, + SADB_EXT_LIFETIME_CURRENT, SADB_EXT_ADDRESS_SRC, + SADB_EXT_ADDRESS_DST, SADB_EXT_ADDRESS_PROXY, SADB_EXT_KEY_AUTH, + SADB_EXT_KEY_ENCRYPT, SADB_EXT_IDENTITY_SRC, + SADB_EXT_IDENTITY_DST, SADB_EXT_SENSITIVITY, + }; - tlen = key_getmsglen(sav); + m = key_setsadbmsg(type, 0, satype, seq, pid, sav->refcnt); + if (m == NULL) + goto fail; + result = m; - p = key_setsadbmsg((caddr_t)newmsg, type, tlen, - satype, seq, pid, - sav->sah->saidx.mode, sav->refcnt); - - for (i = 1; i <= SADB_EXT_MAX; i++) { - switch (i) { + for (i = sizeof(dumporder)/sizeof(dumporder[0]) - 1; i >= 0; i--) { + m = NULL; + p = NULL; + switch (dumporder[i]) { case SADB_EXT_SA: - p = key_setsadbsa(p, sav); + m = key_setsadbsa(sav); + if (!m) + goto fail; break; + case SADB_X_EXT_SA2: + m = key_setsadbxsa2(sav->sah->saidx.mode, + sav->sah->saidx.reqid); + if (!m) + goto fail; + break; + case SADB_EXT_ADDRESS_SRC: - p = key_setsadbaddr(p, - SADB_EXT_ADDRESS_SRC, - (struct sockaddr *)&sav->sah->saidx.src, - _INALENBYAF(sav->sah->saidx.src.ss_family) << 3, - IPSEC_ULPROTO_ANY); + m = key_setsadbaddr(SADB_EXT_ADDRESS_SRC, + (struct sockaddr *)&sav->sah->saidx.src, + sav->sah->saidx.src.ss_len << 3, IPSEC_ULPROTO_ANY); + if (!m) + goto fail; break; case SADB_EXT_ADDRESS_DST: - p = key_setsadbaddr(p, - SADB_EXT_ADDRESS_DST, - (struct sockaddr *)&sav->sah->saidx.dst, - _INALENBYAF(sav->sah->saidx.dst.ss_family) << 3, - IPSEC_ULPROTO_ANY); + m = key_setsadbaddr(SADB_EXT_ADDRESS_DST, + (struct sockaddr *)&sav->sah->saidx.dst, + sav->sah->saidx.dst.ss_len << 3, IPSEC_ULPROTO_ANY); + if (!m) + goto fail; break; case SADB_EXT_KEY_AUTH: - { - u_int len; - if (sav->key_auth == NULL) break; - len = sav->key_auth->sadb_key_len; /* real length */ - bcopy((caddr_t)sav->key_auth, p, len); - ((struct sadb_ext *)p)->sadb_ext_len = PFKEY_UNIT64(len); - p += len; - } + if (!sav->key_auth) + continue; + l = PFKEY_UNUNIT64(sav->key_auth->sadb_key_len); + p = sav->key_auth; break; case SADB_EXT_KEY_ENCRYPT: - { - u_int len; - if (sav->key_enc == NULL) break; - len = sav->key_enc->sadb_key_len; /* real length */ - bcopy((caddr_t)sav->key_enc, p, len); - ((struct sadb_ext *)p)->sadb_ext_len = PFKEY_UNIT64(len); - p += len; - } - break;; + if (!sav->key_enc) + continue; + l = PFKEY_UNUNIT64(sav->key_enc->sadb_key_len); + p = sav->key_enc; + break; case SADB_EXT_LIFETIME_CURRENT: - if (sav->lft_c == NULL) break; - p = key_setsadbext(p, (caddr_t)sav->lft_c); + if (!sav->lft_c) + continue; + l = PFKEY_UNUNIT64(((struct sadb_ext *)sav->lft_c)->sadb_ext_len); + p = sav->lft_c; break; case SADB_EXT_LIFETIME_HARD: - if (sav->lft_h == NULL) break; - p = key_setsadbext(p, (caddr_t)sav->lft_h); + if (!sav->lft_h) + continue; + l = PFKEY_UNUNIT64(((struct sadb_ext *)sav->lft_h)->sadb_ext_len); + p = sav->lft_h; break; case SADB_EXT_LIFETIME_SOFT: - if (sav->lft_s == NULL) break; - p = key_setsadbext(p, (caddr_t)sav->lft_s); + if (!sav->lft_s) + continue; + l = PFKEY_UNUNIT64(((struct sadb_ext *)sav->lft_s)->sadb_ext_len); + p = sav->lft_s; break; + case SADB_EXT_ADDRESS_PROXY: case SADB_EXT_IDENTITY_SRC: case SADB_EXT_IDENTITY_DST: /* XXX: should we brought from SPD ? */ case SADB_EXT_SENSITIVITY: default: - break; + continue; } + + if ((!m && !p) || (m && p)) + goto fail; + if (p && tres) { + M_PREPEND(tres, l, M_DONTWAIT); + if (!tres) + goto fail; + bcopy(p, mtod(tres, caddr_t), l); + continue; + } + if (p) { + m = key_alloc_mbuf(l); + if (!m) + goto fail; + m_copyback(m, 0, l, p); + } + + if (tres) + m_cat(m, tres); + tres = m; } - return tlen; + m_cat(result, tres); + + if (result->m_len < sizeof(struct sadb_msg)) { + result = m_pullup(result, sizeof(struct sadb_msg)); + if (result == NULL) + goto fail; + } + + result->m_pkthdr.len = 0; + for (m = result; m; m = m->m_next) + result->m_pkthdr.len += m->m_len; + + mtod(result, struct sadb_msg *)->sadb_msg_len = + PFKEY_UNIT64(result->m_pkthdr.len); + + return result; + +fail: + m_freem(result); + m_freem(tres); + return NULL; } /* * set data into sadb_msg. - * `buf' must has been allocated sufficiently. */ -static caddr_t -key_setsadbmsg(buf, type, tlen, satype, seq, pid, reserved1, reserved2) - caddr_t buf; +static struct mbuf * +key_setsadbmsg(type, tlen, satype, seq, pid, reserved) u_int8_t type, satype; u_int16_t tlen; u_int32_t seq; pid_t pid; - u_int8_t reserved1; - u_int8_t reserved2; + u_int16_t reserved; { + struct mbuf *m; struct sadb_msg *p; - u_int len; + int len; - p = (struct sadb_msg *)buf; - len = sizeof(struct sadb_msg); + len = PFKEY_ALIGN8(sizeof(struct sadb_msg)); + if (len > MCLBYTES) + return NULL; + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m && len > MHLEN) { + MCLGET(m, M_DONTWAIT); + if ((m->m_flags & M_EXT) == 0) { + m_freem(m); + m = NULL; + } + } + if (!m) + return NULL; + m->m_pkthdr.len = m->m_len = len; + m->m_next = NULL; + p = mtod(m, struct sadb_msg *); + bzero(p, len); p->sadb_msg_version = PF_KEY_V2; p->sadb_msg_type = type; p->sadb_msg_errno = 0; p->sadb_msg_satype = satype; p->sadb_msg_len = PFKEY_UNIT64(tlen); - p->sadb_msg_mode = reserved1; - p->sadb_msg_reserved = reserved2; + p->sadb_msg_reserved = reserved; p->sadb_msg_seq = seq; p->sadb_msg_pid = (u_int32_t)pid; - return(buf + len); + return m; } /* * copy secasvar data into sadb_address. - * `buf' must has been allocated sufficiently. */ -static caddr_t -key_setsadbsa(buf, sav) - caddr_t buf; +static struct mbuf * +key_setsadbsa(sav) struct secasvar *sav; { + struct mbuf *m; struct sadb_sa *p; - u_int len; + int len; - p = (struct sadb_sa *)buf; - len = sizeof(struct sadb_sa); + len = PFKEY_ALIGN8(sizeof(struct sadb_sa)); + m = key_alloc_mbuf(len); + if (!m || m->m_next) { /*XXX*/ + if (m) + m_freem(m); + return NULL; + } + p = mtod(m, struct sadb_sa *); + bzero(p, len); p->sadb_sa_len = PFKEY_UNIT64(len); p->sadb_sa_exttype = SADB_EXT_SA; p->sadb_sa_spi = sav->spi; p->sadb_sa_replay = (sav->replay != NULL ? sav->replay->wsize : 0); p->sadb_sa_state = sav->state; p->sadb_sa_auth = sav->alg_auth; p->sadb_sa_encrypt = sav->alg_enc; p->sadb_sa_flags = sav->flags; - return(buf + len); + return m; } /* * set data into sadb_address. - * `buf' must has been allocated sufficiently. */ -static caddr_t -key_setsadbaddr(buf, exttype, saddr, prefixlen, ul_proto) - caddr_t buf; +static struct mbuf * +key_setsadbaddr(exttype, saddr, prefixlen, ul_proto) u_int16_t exttype; struct sockaddr *saddr; u_int8_t prefixlen; u_int16_t ul_proto; { + struct mbuf *m; struct sadb_address *p; - u_int len; + size_t len; - p = (struct sadb_address *)buf; - len = sizeof(struct sadb_address) + PFKEY_ALIGN8(saddr->sa_len); + len = PFKEY_ALIGN8(sizeof(struct sadb_address)) + + PFKEY_ALIGN8(saddr->sa_len); + m = key_alloc_mbuf(len); + if (!m || m->m_next) { /*XXX*/ + if (m) + m_freem(m); + return NULL; + } + p = mtod(m, struct sadb_address *); + bzero(p, len); p->sadb_address_len = PFKEY_UNIT64(len); p->sadb_address_exttype = exttype; p->sadb_address_proto = ul_proto; p->sadb_address_prefixlen = prefixlen; p->sadb_address_reserved = 0; - bcopy(saddr, p + 1, saddr->sa_len); + bcopy(saddr, + mtod(m, caddr_t) + PFKEY_ALIGN8(sizeof(struct sadb_address)), + saddr->sa_len); - return(buf + len); + return m; } +#if 0 /* * set data into sadb_ident. - * `buf' must has been allocated sufficiently. */ -static caddr_t -key_setsadbident(buf, exttype, idtype, string, stringlen, id) - caddr_t buf; +static struct mbuf * +key_setsadbident(exttype, idtype, string, stringlen, id) u_int16_t exttype, idtype; caddr_t string; int stringlen; u_int64_t id; { + struct mbuf *m; struct sadb_ident *p; - u_int len; + size_t len; - p = (struct sadb_ident *)buf; - len = sizeof(struct sadb_ident) + PFKEY_ALIGN8(stringlen); + len = PFKEY_ALIGN8(sizeof(struct sadb_ident)) + PFKEY_ALIGN8(stringlen); + m = key_alloc_mbuf(len); + if (!m || m->m_next) { /*XXX*/ + if (m) + m_freem(m); + return NULL; + } + p = mtod(m, struct sadb_ident *); + bzero(p, len); p->sadb_ident_len = PFKEY_UNIT64(len); p->sadb_ident_exttype = exttype; p->sadb_ident_type = idtype; p->sadb_ident_reserved = 0; p->sadb_ident_id = id; - bcopy(string, p + 1, stringlen); + bcopy(string, + mtod(m, caddr_t) + PFKEY_ALIGN8(sizeof(struct sadb_ident)), + stringlen); - return(buf + len); + return m; } +#endif /* - * copy buffer of any sadb extension type into sadb_ext. - * assume that sadb_ext_len shifted down >> 3. - * i.e. shift length up when setting length of extension. + * set data into sadb_x_sa2. */ -static caddr_t -key_setsadbext(p, ext) - caddr_t p, ext; +static struct mbuf * +key_setsadbxsa2(mode, reqid) + u_int8_t mode; + u_int32_t reqid; { - u_int len; + struct mbuf *m; + struct sadb_x_sa2 *p; + size_t len; - len = PFKEY_UNUNIT64(((struct sadb_ext *)ext)->sadb_ext_len); + len = PFKEY_ALIGN8(sizeof(struct sadb_x_sa2)); + m = key_alloc_mbuf(len); + if (!m || m->m_next) { /*XXX*/ + if (m) + m_freem(m); + return NULL; + } - bcopy(ext, p, len); + p = mtod(m, struct sadb_x_sa2 *); - return(p + len); + bzero(p, len); + p->sadb_x_sa2_len = PFKEY_UNIT64(len); + p->sadb_x_sa2_exttype = SADB_X_EXT_SA2; + p->sadb_x_sa2_mode = mode; + p->sadb_x_sa2_reserved1 = 0; + p->sadb_x_sa2_reserved2 = 0; + p->sadb_x_sa2_reserved3 = 0; + p->sadb_x_sa2_reqid = reqid; + + return m; } +/* + * set data into sadb_x_policy + */ +static struct mbuf * +key_setsadbxpolicy(type, dir, id) + u_int16_t type; + u_int8_t dir; + u_int32_t id; +{ + struct mbuf *m; + struct sadb_x_policy *p; + size_t len; + + len = PFKEY_ALIGN8(sizeof(struct sadb_x_policy)); + m = key_alloc_mbuf(len); + if (!m || m->m_next) { /*XXX*/ + if (m) + m_freem(m); + return NULL; + } + + p = mtod(m, struct sadb_x_policy *); + + bzero(p, len); + p->sadb_x_policy_len = PFKEY_UNIT64(len); + p->sadb_x_policy_exttype = SADB_X_EXT_POLICY; + p->sadb_x_policy_type = type; + p->sadb_x_policy_dir = dir; + p->sadb_x_policy_id = id; + + return m; +} + /* %%% utilities */ /* * copy a buffer into the new buffer allocated. */ static void * key_newbuf(src, len) - void *src; + const void *src; u_int len; { caddr_t new; KMALLOC(new, caddr_t, len); if (new == NULL) { +#ifdef IPSEC_DEBUG printf("key_newbuf: No more memory.\n"); +#endif return NULL; } - bcopy((caddr_t)src, new, len); + bcopy(src, new, len); return new; } /* compare my own address * OUT: 1: true, i.e. my address. * 0: false */ int -key_ismyaddr(family, addr) - u_int family; - caddr_t addr; +key_ismyaddr(sa) + struct sockaddr *sa; { +#ifdef INET + struct sockaddr_in *sin; + struct in_ifaddr *ia; +#endif + /* sanity check */ - if (addr == NULL) + if (sa == NULL) panic("key_ismyaddr: NULL pointer is passed.\n"); - switch (family) { + switch (sa->sa_family) { +#ifdef INET case AF_INET: - { - struct in_ifaddr *ia; - + sin = (struct sockaddr_in *)sa; for (ia = in_ifaddrhead.tqh_first; ia; ia = ia->ia_link.tqe_next) - if (bcmp(addr, - (caddr_t)&ia->ia_addr.sin_addr, - _INALENBYAF(family)) == 0) + { + if (sin->sin_family == ia->ia_addr.sin_family && + sin->sin_len == ia->ia_addr.sin_len && + sin->sin_addr.s_addr == ia->ia_addr.sin_addr.s_addr) + { return 1; - } + } + } break; +#endif #ifdef INET6 case AF_INET6: - return key_ismyaddr6(addr); + return key_ismyaddr6((struct sockaddr_in6 *)sa); #endif } return 0; } #ifdef INET6 /* * compare my own address for IPv6. * 1: ours * 0: other * NOTE: derived ip6_input() in KAME. This is necessary to modify more. */ -#include #include static int -key_ismyaddr6(addr) - caddr_t addr; +key_ismyaddr6(sin6) + struct sockaddr_in6 *sin6; { - struct in6_addr *a = (struct in6_addr *)addr; struct in6_ifaddr *ia; + struct in6_multi *in6m; for (ia = in6_ifaddr; ia; ia = ia->ia_next) { - if (bcmp(addr, (caddr_t)&ia->ia_addr.sin6_addr, - _INALENBYAF(AF_INET6)) == 0) { + if (key_sockaddrcmp((struct sockaddr *)&sin6, + (struct sockaddr *)&ia->ia_addr, 0) == 0) return 1; - } - /* XXX Multicast */ - { - struct in6_multi *in6m = 0; - - IN6_LOOKUP_MULTI(*(struct in6_addr *)addr, ia->ia_ifp, in6m); + /* + * XXX Multicast + * XXX why do we care about multlicast here while we don't care + * about IPv4 multicast?? + * XXX scope + */ + in6m = NULL; + IN6_LOOKUP_MULTI(sin6->sin6_addr, ia->ia_ifp, in6m); if (in6m) return 1; - } } /* loopback, just for safety */ - if (IN6_IS_ADDR_LOOPBACK(a)) + if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr)) return 1; - /* XXX anycast */ - return 0; } #endif /*INET6*/ /* * compare two secasindex structure exactly. * IN: * saidx0: source, it can be in SAD. - * saidx1: object, it can be from SPD. + * saidx1: object. * OUT: * 1 : equal * 0 : not equal */ static int key_cmpsaidx_exactly(saidx0, saidx1) struct secasindex *saidx0, *saidx1; { /* sanity */ if (saidx0 == NULL && saidx1 == NULL) return 1; if (saidx0 == NULL || saidx1 == NULL) return 0; if (saidx0->proto != saidx1->proto - || saidx0->mode != saidx1->mode) + || saidx0->mode != saidx1->mode + || saidx0->reqid != saidx1->reqid) return 0; - if (bcmp(&saidx0->src, &saidx1->src, saidx0->src.ss_len) != 0 - || bcmp(&saidx0->dst, &saidx1->dst, saidx0->dst.ss_len) != 0) + if (bcmp(&saidx0->src, &saidx1->src, saidx0->src.ss_len) != 0 || + bcmp(&saidx0->dst, &saidx1->dst, saidx0->dst.ss_len) != 0) return 0; return 1; } /* * compare two secasindex structure with consideration mode. * don't compare port. * IN: * saidx0: source, it is often in SAD. * saidx1: object, it is often from SPD. * OUT: * 1 : equal * 0 : not equal */ static int key_cmpsaidx_withmode(saidx0, saidx1) struct secasindex *saidx0, *saidx1; { /* sanity */ if (saidx0 == NULL && saidx1 == NULL) return 1; if (saidx0 == NULL || saidx1 == NULL) return 0; - if (saidx0->proto != saidx1->proto - || saidx0->src.ss_family != saidx1->src.ss_family - || saidx0->dst.ss_family != saidx1->dst.ss_family) + if (saidx0->proto != saidx1->proto) return 0; - if (saidx0->mode != IPSEC_MODE_ANY - && saidx0->mode != saidx1->mode) + /* + * If reqid of SPD is non-zero, unique SA is required. + * The result must be of same reqid in this case. + */ + if (saidx1->reqid != 0 && saidx0->reqid != saidx1->reqid) return 0; - { - int sa_len = _INALENBYAF(saidx0->src.ss_family); + if (saidx0->mode != IPSEC_MODE_ANY && saidx0->mode != saidx1->mode) + return 0; - if (bcmp(_INADDRBYSA(&saidx0->src), _INADDRBYSA(&saidx1->src), sa_len) - || bcmp(_INADDRBYSA(&saidx0->dst), _INADDRBYSA(&saidx1->dst), sa_len)) + if (key_sockaddrcmp((struct sockaddr *)&saidx0->src, + (struct sockaddr *)&saidx1->src, 0) != 0) { return 0; - } + } + if (key_sockaddrcmp((struct sockaddr *)&saidx0->dst, + (struct sockaddr *)&saidx1->dst, 0) != 0) { + return 0; + } return 1; } /* + * compare two secasindex structure without mode. + * don't compare port. + * IN: + * saidx0: source, it is often in SAD. + * saidx1: object, it is often from user. + * OUT: + * 1 : equal + * 0 : not equal + */ +static int +key_cmpsaidx_withoutmode(saidx0, saidx1) + struct secasindex *saidx0, *saidx1; +{ + /* sanity */ + if (saidx0 == NULL && saidx1 == NULL) + return 1; + + if (saidx0 == NULL || saidx1 == NULL) + return 0; + + if (saidx0->proto != saidx1->proto) + return 0; + + if (key_sockaddrcmp((struct sockaddr *)&saidx0->src, + (struct sockaddr *)&saidx1->src, 0) != 0) { + return 0; + } + if (key_sockaddrcmp((struct sockaddr *)&saidx0->dst, + (struct sockaddr *)&saidx1->dst, 0) != 0) { + return 0; + } + + return 1; +} + +/* * compare two secindex structure exactly. * IN: * spidx0: source, it is often in SPD. * spidx1: object, it is often from PFKEY message. * OUT: * 1 : equal * 0 : not equal */ static int key_cmpspidx_exactly(spidx0, spidx1) struct secpolicyindex *spidx0, *spidx1; { /* sanity */ if (spidx0 == NULL && spidx1 == NULL) return 1; if (spidx0 == NULL || spidx1 == NULL) return 0; if (spidx0->prefs != spidx1->prefs || spidx0->prefd != spidx1->prefd || spidx0->ul_proto != spidx1->ul_proto) return 0; - if (bcmp(&spidx0->src, &spidx1->src, spidx0->src.ss_len) != 0 - || bcmp(&spidx0->dst, &spidx1->dst, spidx0->dst.ss_len) != 0) + if (key_sockaddrcmp((struct sockaddr *)&spidx0->src, + (struct sockaddr *)&spidx1->src, 1) != 0) { return 0; + } + if (key_sockaddrcmp((struct sockaddr *)&spidx0->dst, + (struct sockaddr *)&spidx1->dst, 1) != 0) { + return 0; + } return 1; } /* * compare two secindex structure with mask. * IN: * spidx0: source, it is often in SPD. * spidx1: object, it is often from IP header. * OUT: * 1 : equal * 0 : not equal */ static int key_cmpspidx_withmask(spidx0, spidx1) struct secpolicyindex *spidx0, *spidx1; { /* sanity */ if (spidx0 == NULL && spidx1 == NULL) return 1; if (spidx0 == NULL || spidx1 == NULL) return 0; - if (spidx0->src.ss_family != spidx1->src.ss_family - || spidx0->dst.ss_family != spidx1->dst.ss_family) + if (spidx0->src.ss_family != spidx1->src.ss_family || + spidx0->dst.ss_family != spidx1->dst.ss_family || + spidx0->src.ss_len != spidx1->src.ss_len || + spidx0->dst.ss_len != spidx1->dst.ss_len) return 0; /* if spidx.ul_proto == IPSEC_ULPROTO_ANY, ignore. */ if (spidx0->ul_proto != (u_int16_t)IPSEC_ULPROTO_ANY && spidx0->ul_proto != spidx1->ul_proto) return 0; - if (_INPORTBYSA(&spidx0->src) != IPSEC_PORT_ANY - && _INPORTBYSA(&spidx0->src) != _INPORTBYSA(&spidx1->src)) - return 0; + switch (spidx0->src.ss_family) { + case AF_INET: + if (satosin(&spidx0->src)->sin_port != IPSEC_PORT_ANY + && satosin(&spidx0->src)->sin_port != + satosin(&spidx1->src)->sin_port) + return 0; + if (!key_bbcmp((caddr_t)&satosin(&spidx0->src)->sin_addr, + (caddr_t)&satosin(&spidx1->src)->sin_addr, spidx0->prefs)) + return 0; + break; + case AF_INET6: + if (satosin6(&spidx0->src)->sin6_port != IPSEC_PORT_ANY + && satosin6(&spidx0->src)->sin6_port != + satosin6(&spidx1->src)->sin6_port) + return 0; + if (satosin6(&spidx0->src)->sin6_scope_id != + satosin6(&spidx1->src)->sin6_scope_id) + return 0; + if (!key_bbcmp((caddr_t)&satosin6(&spidx0->src)->sin6_addr, + (caddr_t)&satosin6(&spidx1->src)->sin6_addr, spidx0->prefs)) + return 0; + break; + default: + /* XXX */ + if (bcmp(&spidx0->src, &spidx1->src, spidx0->src.ss_len) != 0) + return 0; + break; + } - if (_INPORTBYSA(&spidx0->dst) != IPSEC_PORT_ANY - && _INPORTBYSA(&spidx0->dst) != _INPORTBYSA(&spidx1->dst)) - return 0; + switch (spidx0->dst.ss_family) { + case AF_INET: + if (satosin(&spidx0->dst)->sin_port != IPSEC_PORT_ANY + && satosin(&spidx0->dst)->sin_port != + satosin(&spidx1->dst)->sin_port) + return 0; + if (!key_bbcmp((caddr_t)&satosin(&spidx0->dst)->sin_addr, + (caddr_t)&satosin(&spidx1->dst)->sin_addr, spidx0->prefd)) + return 0; + break; + case AF_INET6: + if (satosin6(&spidx0->dst)->sin6_port != IPSEC_PORT_ANY + && satosin6(&spidx0->dst)->sin6_port != + satosin6(&spidx1->dst)->sin6_port) + return 0; + if (satosin6(&spidx0->dst)->sin6_scope_id != + satosin6(&spidx1->dst)->sin6_scope_id) + return 0; + if (!key_bbcmp((caddr_t)&satosin6(&spidx0->dst)->sin6_addr, + (caddr_t)&satosin6(&spidx1->dst)->sin6_addr, spidx0->prefd)) + return 0; + break; + default: + /* XXX */ + if (bcmp(&spidx0->dst, &spidx1->dst, spidx0->dst.ss_len) != 0) + return 0; + break; + } - if (!key_bbcmp(_INADDRBYSA(&spidx0->src), - _INADDRBYSA(&spidx1->src), - spidx0->prefs)) - return 0; + /* XXX Do we check other field ? e.g. flowinfo */ - if (!key_bbcmp(_INADDRBYSA(&spidx0->dst), - _INADDRBYSA(&spidx1->dst), - spidx0->prefd)) - return 0; + return 1; +} - /* XXX Do we check other field ? e.g. flowinfo, scope_id. */ +/* returns 0 on match */ +static int +key_sockaddrcmp(sa1, sa2, port) + struct sockaddr *sa1; + struct sockaddr *sa2; + int port; +{ + if (sa1->sa_family != sa2->sa_family || sa1->sa_len != sa2->sa_len) + return 1; - return 1; + switch (sa1->sa_family) { + case AF_INET: + if (sa1->sa_len != sizeof(struct sockaddr_in)) + return 1; + if (satosin(sa1)->sin_addr.s_addr != + satosin(sa2)->sin_addr.s_addr) { + return 1; + } + if (port && satosin(sa1)->sin_port != satosin(sa2)->sin_port) + return 1; + break; + case AF_INET6: + if (sa1->sa_len != sizeof(struct sockaddr_in6)) + return 1; /*EINVAL*/ + if (satosin6(sa1)->sin6_scope_id != + satosin6(sa2)->sin6_scope_id) { + return 1; + } + if (!IN6_ARE_ADDR_EQUAL(&satosin6(sa1)->sin6_addr, + &satosin6(sa2)->sin6_addr)) { + return 1; + } + if (port && + satosin6(sa1)->sin6_port != satosin6(sa2)->sin6_port) { + return 1; + } + default: + if (bcmp(sa1, sa2, sa1->sa_len) != 0) + return 1; + break; + } + + return 0; } /* * compare two buffers with mask. * IN: * addr1: source * addr2: object * bits: Number of bits to compare * OUT: * 1 : equal * 0 : not equal */ static int key_bbcmp(p1, p2, bits) - register caddr_t p1, p2; - register u_int bits; + caddr_t p1, p2; + u_int bits; { u_int8_t mask; /* XXX: This could be considerably faster if we compare a word * at a time, but it is complicated on LSB Endian machines */ /* Handle null pointers */ if (p1 == NULL || p2 == NULL) return (p1 == p2); while (bits >= 8) { if (*p1++ != *p2++) return 0; bits -= 8; } if (bits > 0) { mask = ~((1<<(8-bits))-1); if ((*p1 & mask) != (*p2 & mask)) return 0; } return 1; /* Match! */ } /* * time handler. * scanning SPD and SAD to check status for each entries, * and do to remove or to expire. */ void key_timehandler(void) { u_int dir; int s; s = splnet(); /*called from softclock()*/ /* SPD */ { struct secpolicy *sp, *nextsp; for (dir = 0; dir < IPSEC_DIR_MAX; dir++) { for (sp = LIST_FIRST(&sptree[dir]); sp != NULL; sp = nextsp) { nextsp = LIST_NEXT(sp, chain); if (sp->state == IPSEC_SPSTATE_DEAD) key_freesp(sp); } } } /* SAD */ { struct secashead *sah, *nextsah; struct secasvar *sav, *nextsav; for (sah = LIST_FIRST(&sahtree); sah != NULL; sah = nextsah) { nextsah = LIST_NEXT(sah, chain); /* if sah has been dead, then delete it and process next sah. */ if (sah->state == SADB_SASTATE_DEAD) { key_delsah(sah); continue; } /* if LARVAL entry doesn't become MATURE, delete it. */ for (sav = LIST_FIRST(&sah->savtree[SADB_SASTATE_LARVAL]); sav != NULL; sav = nextsav) { nextsav = LIST_NEXT(sav, chain); sav->tick++; if (key_larval_lifetime < sav->tick) { key_freesav(sav); } } /* * check MATURE entry to start to send expire message * whether or not. */ for (sav = LIST_FIRST(&sah->savtree[SADB_SASTATE_MATURE]); sav != NULL; sav = nextsav) { nextsav = LIST_NEXT(sav, chain); sav->tick++; /* we don't need to check. */ if (sav->lft_s == NULL) continue; /* sanity check */ if (sav->lft_c == NULL) { +#ifdef IPSEC_DEBUG printf("key_timehandler: " "There is no CURRENT time, why?\n"); +#endif continue; } /* compare SOFT lifetime and tick */ if (sav->lft_s->sadb_lifetime_addtime != 0 && sav->lft_s->sadb_lifetime_addtime < sav->tick) { /* * check SA to be used whether or not. * when SA hasn't been used, delete it. */ if (sav->lft_c->sadb_lifetime_usetime == 0) { key_sa_chgstate(sav, SADB_SASTATE_DEAD); key_freesav(sav); sav = NULL; } else { key_sa_chgstate(sav, SADB_SASTATE_DYING); /* * XXX If we keep to send expire * message in the status of * DYING. Do remove below code. */ key_expire(sav); } } /* check SOFT lifetime by bytes */ /* * XXX I don't know the way to delete this SA * when new SA is installed. Caution when it's * installed too big lifetime by time. */ else if (sav->lft_s->sadb_lifetime_bytes != 0 && sav->lft_s->sadb_lifetime_bytes < sav->lft_c->sadb_lifetime_bytes) { key_sa_chgstate(sav, SADB_SASTATE_DYING); /* * XXX If we keep to send expire * message in the status of * DYING. Do remove below code. */ key_expire(sav); } } /* check DYING entry to change status to DEAD. */ for (sav = LIST_FIRST(&sah->savtree[SADB_SASTATE_DYING]); sav != NULL; sav = nextsav) { nextsav = LIST_NEXT(sav, chain); sav->tick++; /* we don't need to check. */ if (sav->lft_h == NULL) continue; /* sanity check */ if (sav->lft_c == NULL) { +#ifdef IPSEC_DEBUG printf("key_timehandler: " "There is no CURRENT time, why?\n"); +#endif continue; } /* compare HARD lifetime and tick */ if (sav->lft_h->sadb_lifetime_addtime != 0 && sav->lft_h->sadb_lifetime_addtime < sav->tick) { key_sa_chgstate(sav, SADB_SASTATE_DEAD); key_freesav(sav); sav = NULL; } +#if 0 /* XXX Should we keep to send expire message until HARD lifetime ? */ + else if (sav->lft_s != NULL + && sav->lft_s->sadb_lifetime_addtime != 0 + && sav->lft_s->sadb_lifetime_addtime < sav->tick) { + /* + * XXX: should be checked to be + * installed the valid SA. + */ + + /* + * If there is no SA then sending + * expire message. + */ + key_expire(sav); + } +#endif /* check HARD lifetime by bytes */ else if (sav->lft_h->sadb_lifetime_bytes != 0 && sav->lft_h->sadb_lifetime_bytes < sav->lft_c->sadb_lifetime_bytes) { key_sa_chgstate(sav, SADB_SASTATE_DEAD); key_freesav(sav); sav = NULL; } } /* delete entry in DEAD */ for (sav = LIST_FIRST(&sah->savtree[SADB_SASTATE_DEAD]); sav != NULL; sav = nextsav) { nextsav = LIST_NEXT(sav, chain); /* sanity check */ if (sav->state != SADB_SASTATE_DEAD) { +#ifdef IPSEC_DEBUG printf("key_timehandler: " "invalid sav->state " "(queue: %d SA: %d): " "kill it anyway\n", SADB_SASTATE_DEAD, sav->state); +#endif } /* * do not call key_freesav() here. * sav should already be freed, and sav->refcnt * shows other references to sav * (such as from SPD). */ } } } #ifndef IPSEC_NONBLOCK_ACQUIRE /* ACQ tree */ { struct secacq *acq, *nextacq; for (acq = LIST_FIRST(&acqtree); acq != NULL; acq = nextacq) { nextacq = LIST_NEXT(acq, chain); acq->tick++; if (key_blockacq_lifetime < acq->tick && __LIST_CHAINED(acq)) { LIST_REMOVE(acq, chain); KFREE(acq); } } } #endif + /* SP ACQ tree */ + { + struct secspacq *acq, *nextacq; + + for (acq = LIST_FIRST(&spacqtree); + acq != NULL; + acq = nextacq) { + + nextacq = LIST_NEXT(acq, chain); + + acq->tick++; + + if (key_blockacq_lifetime < acq->tick && __LIST_CHAINED(acq)) { + LIST_REMOVE(acq, chain); + KFREE(acq); + } + } + } + /* initialize random seed */ if (key_tick_init_random++ > key_int_random) { key_tick_init_random = 0; key_srandom(); } #ifndef IPSEC_DEBUG2 /* do exchange to tick time !! */ (void)timeout((void *)key_timehandler, (void *)0, 100); #endif /* IPSEC_DEBUG2 */ splx(s); return; } /* * to initialize a seed for random() */ -void +static void key_srandom() { struct timeval tv; microtime(&tv); + srandom(tv.tv_usec); return; } /* + * to initialize a seed for random() + */ +static u_long +key_random() +{ + u_long value; + + value = random(); + + return value; +} + +/* * map SADB_SATYPE_* to IPPROTO_*. * if satype == SADB_SATYPE then satype is mapped to ~0. * OUT: * 0: invalid satype. */ static u_int16_t key_satype2proto(satype) u_int8_t satype; { switch (satype) { case SADB_SATYPE_UNSPEC: return IPSEC_PROTO_ANY; case SADB_SATYPE_AH: return IPPROTO_AH; case SADB_SATYPE_ESP: return IPPROTO_ESP; +#if 1 /*nonstandard*/ + case SADB_X_SATYPE_IPCOMP: + return IPPROTO_IPCOMP; + break; +#endif default: return 0; } /* NOTREACHED */ } /* * map IPPROTO_* to SADB_SATYPE_* * OUT: * 0: invalid protocol type. */ static u_int8_t key_proto2satype(proto) u_int16_t proto; { switch (proto) { case IPPROTO_AH: return SADB_SATYPE_AH; case IPPROTO_ESP: return SADB_SATYPE_ESP; +#if 1 /*nonstandard*/ + case IPPROTO_IPCOMP: + return SADB_X_SATYPE_IPCOMP; + break; +#endif default: return 0; } /* NOTREACHED */ } /* %%% PF_KEY */ /* * SADB_GETSPI processing is to receive - * + * * from the IKMPd, to assign a unique spi value, to hang on the INBOUND * tree with the status of LARVAL, and send * * to the IKMPd. * * IN: mhp: pointer to the pointer to each header. * OUT: NULL if fail. * other if success, return pointer to the message to send. */ -static struct sadb_msg * -key_getspi(mhp) - caddr_t *mhp; +static int +key_getspi(so, m, mhp) + struct socket *so; + struct mbuf *m; + const struct sadb_msghdr *mhp; { - struct sadb_msg *msg0; struct sadb_address *src0, *dst0; struct secasindex saidx; struct secashead *newsah; struct secasvar *newsav; u_int8_t proto; u_int32_t spi; + u_int8_t mode; + u_int32_t reqid; + int error; /* sanity check */ - if (mhp == NULL || mhp[0] == NULL) + if (so == NULL || m == NULL || mhp == NULL || mhp->msg == NULL) panic("key_getspi: NULL pointer is passed.\n"); - msg0 = (struct sadb_msg *)mhp[0]; - - if (mhp[SADB_EXT_ADDRESS_SRC] == NULL - || mhp[SADB_EXT_ADDRESS_DST] == NULL) { + if (mhp->ext[SADB_EXT_ADDRESS_SRC] == NULL || + mhp->ext[SADB_EXT_ADDRESS_DST] == NULL) { +#ifdef IPSEC_DEBUG printf("key_getspi: invalid message is passed.\n"); - msg0->sadb_msg_errno = EINVAL; - return NULL; +#endif + return key_senderror(so, m, EINVAL); } + if (mhp->extlen[SADB_EXT_ADDRESS_SRC] < sizeof(struct sadb_address) || + mhp->extlen[SADB_EXT_ADDRESS_DST] < sizeof(struct sadb_address)) { +#ifdef IPSEC_DEBUG + printf("key_getspi: invalid message is passed.\n"); +#endif + return key_senderror(so, m, EINVAL); + } + if (mhp->ext[SADB_X_EXT_SA2] != NULL) { + mode = ((struct sadb_x_sa2 *)mhp->ext[SADB_X_EXT_SA2])->sadb_x_sa2_mode; + reqid = ((struct sadb_x_sa2 *)mhp->ext[SADB_X_EXT_SA2])->sadb_x_sa2_reqid; + } else { + mode = IPSEC_MODE_ANY; + reqid = 0; + } - src0 = (struct sadb_address *)(mhp[SADB_EXT_ADDRESS_SRC]); - dst0 = (struct sadb_address *)(mhp[SADB_EXT_ADDRESS_DST]); + src0 = (struct sadb_address *)(mhp->ext[SADB_EXT_ADDRESS_SRC]); + dst0 = (struct sadb_address *)(mhp->ext[SADB_EXT_ADDRESS_DST]); /* map satype to proto */ - if ((proto = key_satype2proto(msg0->sadb_msg_satype)) == 0) { + if ((proto = key_satype2proto(mhp->msg->sadb_msg_satype)) == 0) { +#ifdef IPSEC_DEBUG printf("key_getspi: invalid satype is passed.\n"); - msg0->sadb_msg_errno = EINVAL; - return NULL; +#endif + return key_senderror(so, m, EINVAL); } - KEY_SETSECASIDX(proto, msg0->sadb_msg_mode, src0+1, dst0+1, &saidx); + /* make sure if port number is zero. */ + switch (((struct sockaddr *)(src0 + 1))->sa_family) { + case AF_INET: + if (((struct sockaddr *)(src0 + 1))->sa_len != + sizeof(struct sockaddr_in)) + return key_senderror(so, m, EINVAL); + ((struct sockaddr_in *)(src0 + 1))->sin_port = 0; + break; + case AF_INET6: + if (((struct sockaddr *)(src0 + 1))->sa_len != + sizeof(struct sockaddr_in6)) + return key_senderror(so, m, EINVAL); + ((struct sockaddr_in6 *)(src0 + 1))->sin6_port = 0; + break; + default: + ; /*???*/ + } + switch (((struct sockaddr *)(dst0 + 1))->sa_family) { + case AF_INET: + if (((struct sockaddr *)(dst0 + 1))->sa_len != + sizeof(struct sockaddr_in)) + return key_senderror(so, m, EINVAL); + ((struct sockaddr_in *)(dst0 + 1))->sin_port = 0; + break; + case AF_INET6: + if (((struct sockaddr *)(dst0 + 1))->sa_len != + sizeof(struct sockaddr_in6)) + return key_senderror(so, m, EINVAL); + ((struct sockaddr_in6 *)(dst0 + 1))->sin6_port = 0; + break; + default: + ; /*???*/ + } + /* XXX boundary check against sa_len */ + KEY_SETSECASIDX(proto, mode, reqid, src0 + 1, dst0 + 1, &saidx); + /* SPI allocation */ - spi = key_do_getnewspi((struct sadb_spirange *)mhp[SADB_EXT_SPIRANGE], + spi = key_do_getnewspi((struct sadb_spirange *)mhp->ext[SADB_EXT_SPIRANGE], &saidx); - if (spi == 0) { - msg0->sadb_msg_errno = EEXIST; - return NULL; - } + if (spi == 0) + return key_senderror(so, m, EINVAL); /* get a SA index */ if ((newsah = key_getsah(&saidx)) == NULL) { - /* create a new SA index */ if ((newsah = key_newsah(&saidx)) == NULL) { +#ifdef IPSEC_DEBUG printf("key_getspi: No more memory.\n"); - msg0->sadb_msg_errno = ENOBUFS; - return NULL; +#endif + return key_senderror(so, m, ENOBUFS); } } /* get a new SA */ - if ((newsav = key_newsav(mhp, newsah)) == NULL) { - msg0->sadb_msg_errno = ENOBUFS; + /* XXX rewrite */ + newsav = key_newsav(m, mhp, newsah, &error); + if (newsav == NULL) { /* XXX don't free new SA index allocated in above. */ - return NULL; + return key_senderror(so, m, error); } /* set spi */ newsav->spi = htonl(spi); #ifndef IPSEC_NONBLOCK_ACQUIRE /* delete the entry in acqtree */ - if (msg0->sadb_msg_seq != 0) { + if (mhp->msg->sadb_msg_seq != 0) { struct secacq *acq; - if ((acq = key_getacqbyseq(msg0->sadb_msg_seq)) != NULL) { + if ((acq = key_getacqbyseq(mhp->msg->sadb_msg_seq)) != NULL) { /* reset counter in order to deletion by timehander. */ acq->tick = key_blockacq_lifetime; acq->count = 0; } } #endif { + struct mbuf *n, *nn; + struct sadb_sa *m_sa; struct sadb_msg *newmsg; - u_int len; - caddr_t p; + int off, len; /* create new sadb_msg to reply. */ - len = sizeof(struct sadb_msg) - + sizeof(struct sadb_sa) - + PFKEY_EXTLEN(mhp[SADB_EXT_ADDRESS_SRC]) - + PFKEY_EXTLEN(mhp[SADB_EXT_ADDRESS_DST]); + len = PFKEY_ALIGN8(sizeof(struct sadb_msg)) + + PFKEY_ALIGN8(sizeof(struct sadb_sa)); + if (len > MCLBYTES) + return key_senderror(so, m, ENOBUFS); - KMALLOC(newmsg, struct sadb_msg *, len); - if (newmsg == NULL) { - printf("key_getspi: No more memory.\n"); - msg0->sadb_msg_errno = ENOBUFS; - return NULL; + MGETHDR(n, M_DONTWAIT, MT_DATA); + if (len > MHLEN) { + MCLGET(n, M_DONTWAIT); + if ((n->m_flags & M_EXT) == 0) { + m_freem(n); + n = NULL; + } } - bzero((caddr_t)newmsg, len); + if (!n) + return key_senderror(so, m, ENOBUFS); - bcopy((caddr_t)mhp[0], (caddr_t)newmsg, sizeof(*msg0)); - newmsg->sadb_msg_seq = newsav->seq; - newmsg->sadb_msg_errno = 0; - newmsg->sadb_msg_len = PFKEY_UNIT64(len); - p = (caddr_t)newmsg + sizeof(*msg0); + n->m_len = len; + n->m_next = NULL; + off = 0; - { - struct sadb_sa *m_sa; - m_sa = (struct sadb_sa *)p; + m_copydata(m, 0, sizeof(struct sadb_msg), mtod(n, caddr_t) + off); + off += PFKEY_ALIGN8(sizeof(struct sadb_msg)); + + m_sa = (struct sadb_sa *)(mtod(n, caddr_t) + off); m_sa->sadb_sa_len = PFKEY_UNIT64(sizeof(struct sadb_sa)); m_sa->sadb_sa_exttype = SADB_EXT_SA; m_sa->sadb_sa_spi = htonl(spi); - p += sizeof(struct sadb_sa); - } + off += PFKEY_ALIGN8(sizeof(struct sadb_sa)); - p = key_setsadbext(p, mhp[SADB_EXT_ADDRESS_SRC]); - p = key_setsadbext(p, mhp[SADB_EXT_ADDRESS_DST]); +#ifdef DIAGNOSTIC + if (off != len) + panic("length inconsistency in key_getspi"); +#endif - return newmsg; + n->m_next = key_gather_mbuf(m, mhp, 0, 2, SADB_EXT_ADDRESS_SRC, + SADB_EXT_ADDRESS_DST); + if (!n->m_next) { + m_freem(n); + return key_senderror(so, m, ENOBUFS); + } + + if (n->m_len < sizeof(struct sadb_msg)) { + n = m_pullup(n, sizeof(struct sadb_msg)); + if (n == NULL) + return key_sendup_mbuf(so, m, KEY_SENDUP_ONE); + } + + n->m_pkthdr.len = 0; + for (nn = n; nn; nn = nn->m_next) + n->m_pkthdr.len += nn->m_len; + + newmsg = mtod(n, struct sadb_msg *); + newmsg->sadb_msg_seq = newsav->seq; + newmsg->sadb_msg_errno = 0; + newmsg->sadb_msg_len = PFKEY_UNIT64(n->m_pkthdr.len); + + m_freem(m); + return key_sendup_mbuf(so, n, KEY_SENDUP_ONE); } } /* * allocating new SPI * called by key_getspi(). * OUT: * 0: failure. * others: success. */ static u_int32_t key_do_getnewspi(spirange, saidx) struct sadb_spirange *spirange; struct secasindex *saidx; { u_int32_t newspi; u_int32_t min, max; int count = key_spi_trycnt; /* set spi range to allocate */ if (spirange != NULL) { min = spirange->sadb_spirange_min; max = spirange->sadb_spirange_max; } else { min = key_spi_minval; max = key_spi_maxval; } + /* IPCOMP needs 2-byte SPI */ + if (saidx->proto == IPPROTO_IPCOMP) { + u_int32_t t; + if (min >= 0x10000) + min = 0xffff; + if (max >= 0x10000) + max = 0xffff; + if (min > max) { + t = min; min = max; max = t; + } + } if (min == max) { if (key_checkspidup(saidx, min) != NULL) { +#ifdef IPSEC_DEBUG printf("key_do_getnewspi: SPI %u exists already.\n", min); +#endif return 0; } count--; /* taking one cost. */ newspi = min; } else { /* init SPI */ newspi = 0; /* when requesting to allocate spi ranged */ while (count--) { /* generate pseudo-random SPI value ranged. */ - newspi = min + (random() % ( max - min + 1 )); + newspi = min + (key_random() % (max - min + 1)); if (key_checkspidup(saidx, newspi) == NULL) break; } if (count == 0 || newspi == 0) { +#ifdef IPSEC_DEBUG printf("key_do_getnewspi: to allocate spi is failed.\n"); +#endif return 0; } } /* statistics */ keystat.getspi_count = (keystat.getspi_count + key_spi_trycnt - count) / 2; return newspi; } /* * SADB_UPDATE processing * receive - * * from the ikmpd, and update a secasvar entry whose status is SADB_SASTATE_LARVAL. * and send - * * to the ikmpd. * - * IN: mhp: pointer to the pointer to each header. - * OUT: NULL if fail. - * other if success, return pointer to the message to send. + * m will always be freed. */ -static struct sadb_msg * -key_update(mhp) - caddr_t *mhp; +static int +key_update(so, m, mhp) + struct socket *so; + struct mbuf *m; + const struct sadb_msghdr *mhp; { - struct sadb_msg *msg0; struct sadb_sa *sa0; struct sadb_address *src0, *dst0; struct secasindex saidx; struct secashead *sah; struct secasvar *sav; u_int16_t proto; + u_int8_t mode; + u_int32_t reqid; + int error; /* sanity check */ - if (mhp == NULL || mhp[0] == NULL) + if (so == NULL || m == NULL || mhp == NULL || mhp->msg == NULL) panic("key_update: NULL pointer is passed.\n"); - msg0 = (struct sadb_msg *)mhp[0]; - /* map satype to proto */ - if ((proto = key_satype2proto(msg0->sadb_msg_satype)) == 0) { + if ((proto = key_satype2proto(mhp->msg->sadb_msg_satype)) == 0) { +#ifdef IPSEC_DEBUG printf("key_update: invalid satype is passed.\n"); - msg0->sadb_msg_errno = EINVAL; - return NULL; +#endif + return key_senderror(so, m, EINVAL); } - if (mhp[SADB_EXT_SA] == NULL - || mhp[SADB_EXT_ADDRESS_SRC] == NULL - || mhp[SADB_EXT_ADDRESS_DST] == NULL - || (msg0->sadb_msg_satype == SADB_SATYPE_ESP - && mhp[SADB_EXT_KEY_ENCRYPT] == NULL) - || (msg0->sadb_msg_satype == SADB_SATYPE_AH - && mhp[SADB_EXT_KEY_AUTH] == NULL) - || (mhp[SADB_EXT_LIFETIME_HARD] != NULL - && mhp[SADB_EXT_LIFETIME_SOFT] == NULL) - || (mhp[SADB_EXT_LIFETIME_HARD] == NULL - && mhp[SADB_EXT_LIFETIME_SOFT] != NULL)) { + if (mhp->ext[SADB_EXT_SA] == NULL || + mhp->ext[SADB_EXT_ADDRESS_SRC] == NULL || + mhp->ext[SADB_EXT_ADDRESS_DST] == NULL || + (mhp->msg->sadb_msg_satype == SADB_SATYPE_ESP && + mhp->ext[SADB_EXT_KEY_ENCRYPT] == NULL) || + (mhp->msg->sadb_msg_satype == SADB_SATYPE_AH && + mhp->ext[SADB_EXT_KEY_AUTH] == NULL) || + (mhp->ext[SADB_EXT_LIFETIME_HARD] != NULL && + mhp->ext[SADB_EXT_LIFETIME_SOFT] == NULL) || + (mhp->ext[SADB_EXT_LIFETIME_HARD] == NULL && + mhp->ext[SADB_EXT_LIFETIME_SOFT] != NULL)) { +#ifdef IPSEC_DEBUG printf("key_update: invalid message is passed.\n"); - msg0->sadb_msg_errno = EINVAL; - return NULL; +#endif + return key_senderror(so, m, EINVAL); } + if (mhp->extlen[SADB_EXT_SA] < sizeof(struct sadb_sa) || + mhp->extlen[SADB_EXT_ADDRESS_SRC] < sizeof(struct sadb_address) || + mhp->extlen[SADB_EXT_ADDRESS_DST] < sizeof(struct sadb_address)) { +#ifdef IPSEC_DEBUG + printf("key_update: invalid message is passed.\n"); +#endif + return key_senderror(so, m, EINVAL); + } + if (mhp->ext[SADB_X_EXT_SA2] != NULL) { + mode = ((struct sadb_x_sa2 *)mhp->ext[SADB_X_EXT_SA2])->sadb_x_sa2_mode; + reqid = ((struct sadb_x_sa2 *)mhp->ext[SADB_X_EXT_SA2])->sadb_x_sa2_reqid; + } else { + mode = IPSEC_MODE_ANY; + reqid = 0; + } + /* XXX boundary checking for other extensions */ - sa0 = (struct sadb_sa *)mhp[SADB_EXT_SA]; - src0 = (struct sadb_address *)(mhp[SADB_EXT_ADDRESS_SRC]); - dst0 = (struct sadb_address *)(mhp[SADB_EXT_ADDRESS_DST]); + sa0 = (struct sadb_sa *)mhp->ext[SADB_EXT_SA]; + src0 = (struct sadb_address *)(mhp->ext[SADB_EXT_ADDRESS_SRC]); + dst0 = (struct sadb_address *)(mhp->ext[SADB_EXT_ADDRESS_DST]); - KEY_SETSECASIDX(proto, msg0->sadb_msg_mode, src0+1, dst0+1, &saidx); + /* XXX boundary check against sa_len */ + KEY_SETSECASIDX(proto, mode, reqid, src0 + 1, dst0 + 1, &saidx); /* get a SA header */ if ((sah = key_getsah(&saidx)) == NULL) { +#ifdef IPSEC_DEBUG printf("key_update: no SA index found.\n"); - msg0->sadb_msg_errno = ENOENT; - return NULL; +#endif + return key_senderror(so, m, ENOENT); } + /* set spidx if there */ + /* XXX rewrite */ + error = key_setident(sah, m, mhp); + if (error) + return key_senderror(so, m, error); + /* find a SA with sequence number. */ - if ((sav = key_getsavbyseq(sah, msg0->sadb_msg_seq)) == NULL) { +#ifdef IPSEC_DOSEQCHECK + if (mhp->msg->sadb_msg_seq != 0 + && (sav = key_getsavbyseq(sah, mhp->msg->sadb_msg_seq)) == NULL) { +#ifdef IPSEC_DEBUG printf("key_update: no larval SA with sequence %u exists.\n", - msg0->sadb_msg_seq); - msg0->sadb_msg_errno = ENOENT; - return NULL; + mhp->msg->sadb_msg_seq); +#endif + return key_senderror(so, m, ENOENT); } +#else + if ((sav = key_getsavbyspi(sah, sa0->sadb_sa_spi)) == NULL) { +#ifdef IPSEC_DEBUG + printf("key_update: no such a SA found (spi:%u)\n", + (u_int32_t)ntohl(sa0->sadb_sa_spi)); +#endif + return key_senderror(so, m, EINVAL); + } +#endif /* validity check */ if (sav->sah->saidx.proto != proto) { +#ifdef IPSEC_DEBUG printf("key_update: protocol mismatched (DB=%u param=%u)\n", sav->sah->saidx.proto, proto); - msg0->sadb_msg_errno = EINVAL; - return NULL; +#endif + return key_senderror(so, m, EINVAL); } +#ifdef IPSEC_DOSEQCHECK if (sav->spi != sa0->sadb_sa_spi) { +#ifdef IPSEC_DEBUG printf("key_update: SPI mismatched (DB:%u param:%u)\n", (u_int32_t)ntohl(sav->spi), (u_int32_t)ntohl(sa0->sadb_sa_spi)); - msg0->sadb_msg_errno = EINVAL; - return NULL; +#endif + return key_senderror(so, m, EINVAL); } - if (sav->pid != msg0->sadb_msg_pid) { +#endif + if (sav->pid != mhp->msg->sadb_msg_pid) { +#ifdef IPSEC_DEBUG printf("key_update: pid mismatched (DB:%u param:%u)\n", - sav->pid, msg0->sadb_msg_pid); - msg0->sadb_msg_errno = EINVAL; - return NULL; + sav->pid, mhp->msg->sadb_msg_pid); +#endif + return key_senderror(so, m, EINVAL); } /* copy sav values */ - if (key_setsaval(sav, mhp)) { + error = key_setsaval(sav, m, mhp); + if (error) { key_freesav(sav); - return NULL; + return key_senderror(so, m, error); } /* check SA values to be mature. */ - if ((msg0->sadb_msg_errno = key_mature(sav)) != 0) { + if ((mhp->msg->sadb_msg_errno = key_mature(sav)) != 0) { key_freesav(sav); - return NULL; + return key_senderror(so, m, 0); } - /* - * we must call key_freesav() whenever we leave a function context, - * as we did not allocated a new sav (we updated existing sav). - */ - key_freesav(sav); - sav = NULL; - { - struct sadb_msg *newmsg; + struct mbuf *n; /* set msg buf from mhp */ - if ((newmsg = key_getmsgbuf_x1(mhp)) == NULL) { + n = key_getmsgbuf_x1(m, mhp); + if (n == NULL) { +#ifdef IPSEC_DEBUG printf("key_update: No more memory.\n"); - msg0->sadb_msg_errno = ENOBUFS; - return NULL; +#endif + return key_senderror(so, m, ENOBUFS); } - return newmsg; + + m_freem(m); + return key_sendup_mbuf(so, n, KEY_SENDUP_ALL); } } /* * search SAD with sequence for a SA which state is SADB_SASTATE_LARVAL. * only called by key_update(). * OUT: * NULL : not found * others : found, pointer to a SA. */ +#ifdef IPSEC_DOSEQCHECK static struct secasvar * key_getsavbyseq(sah, seq) struct secashead *sah; u_int32_t seq; { struct secasvar *sav; u_int state; state = SADB_SASTATE_LARVAL; /* search SAD with sequence number ? */ - __LIST_FOREACH(sav, &sah->savtree[state], chain) { + LIST_FOREACH(sav, &sah->savtree[state], chain) { KEY_CHKSASTATE(state, sav->state, "key_getsabyseq"); if (sav->seq == seq) { sav->refcnt++; KEYDEBUG(KEYDEBUG_IPSEC_STAMP, printf("DP key_getsavbyseq cause " "refcnt++:%d SA:%p\n", sav->refcnt, sav)); return sav; } } return NULL; } +#endif /* * SADB_ADD processing * add a entry to SA database, when received - * * from the ikmpd, * and send - * * to the ikmpd. * * IGNORE identity and sensitivity messages. * - * IN: mhp: pointer to the pointer to each header. - * OUT: NULL if fail. - * other if success, return pointer to the message to send. + * m will always be freed. */ -static struct sadb_msg * -key_add(mhp) - caddr_t *mhp; +static int +key_add(so, m, mhp) + struct socket *so; + struct mbuf *m; + const struct sadb_msghdr *mhp; { - struct sadb_msg *msg0; struct sadb_sa *sa0; struct sadb_address *src0, *dst0; struct secasindex saidx; struct secashead *newsah; struct secasvar *newsav; u_int16_t proto; + u_int8_t mode; + u_int32_t reqid; + int error; /* sanity check */ - if (mhp == NULL || mhp[0] == NULL) + if (so == NULL || m == NULL || mhp == NULL || mhp->msg == NULL) panic("key_add: NULL pointer is passed.\n"); - msg0 = (struct sadb_msg *)mhp[0]; - /* map satype to proto */ - if ((proto = key_satype2proto(msg0->sadb_msg_satype)) == 0) { + if ((proto = key_satype2proto(mhp->msg->sadb_msg_satype)) == 0) { +#ifdef IPSEC_DEBUG printf("key_add: invalid satype is passed.\n"); - msg0->sadb_msg_errno = EINVAL; - return NULL; +#endif + return key_senderror(so, m, EINVAL); } - if (mhp[SADB_EXT_SA] == NULL - || mhp[SADB_EXT_ADDRESS_SRC] == NULL - || mhp[SADB_EXT_ADDRESS_DST] == NULL - || (msg0->sadb_msg_satype == SADB_SATYPE_ESP - && mhp[SADB_EXT_KEY_ENCRYPT] == NULL) - || (msg0->sadb_msg_satype == SADB_SATYPE_AH - && mhp[SADB_EXT_KEY_AUTH] == NULL) - || (mhp[SADB_EXT_LIFETIME_HARD] != NULL - && mhp[SADB_EXT_LIFETIME_SOFT] == NULL) - || (mhp[SADB_EXT_LIFETIME_HARD] == NULL - && mhp[SADB_EXT_LIFETIME_SOFT] != NULL)) { + if (mhp->ext[SADB_EXT_SA] == NULL || + mhp->ext[SADB_EXT_ADDRESS_SRC] == NULL || + mhp->ext[SADB_EXT_ADDRESS_DST] == NULL || + (mhp->msg->sadb_msg_satype == SADB_SATYPE_ESP && + mhp->ext[SADB_EXT_KEY_ENCRYPT] == NULL) || + (mhp->msg->sadb_msg_satype == SADB_SATYPE_AH && + mhp->ext[SADB_EXT_KEY_AUTH] == NULL) || + (mhp->ext[SADB_EXT_LIFETIME_HARD] != NULL && + mhp->ext[SADB_EXT_LIFETIME_SOFT] == NULL) || + (mhp->ext[SADB_EXT_LIFETIME_HARD] == NULL && + mhp->ext[SADB_EXT_LIFETIME_SOFT] != NULL)) { +#ifdef IPSEC_DEBUG printf("key_add: invalid message is passed.\n"); - msg0->sadb_msg_errno = EINVAL; - return NULL; +#endif + return key_senderror(so, m, EINVAL); } + if (mhp->extlen[SADB_EXT_SA] < sizeof(struct sadb_sa) || + mhp->extlen[SADB_EXT_ADDRESS_SRC] < sizeof(struct sadb_address) || + mhp->extlen[SADB_EXT_ADDRESS_DST] < sizeof(struct sadb_address)) { + /* XXX need more */ +#ifdef IPSEC_DEBUG + printf("key_add: invalid message is passed.\n"); +#endif + return key_senderror(so, m, EINVAL); + } + if (mhp->ext[SADB_X_EXT_SA2] != NULL) { + mode = ((struct sadb_x_sa2 *)mhp->ext[SADB_X_EXT_SA2])->sadb_x_sa2_mode; + reqid = ((struct sadb_x_sa2 *)mhp->ext[SADB_X_EXT_SA2])->sadb_x_sa2_reqid; + } else { + mode = IPSEC_MODE_ANY; + reqid = 0; + } - sa0 = (struct sadb_sa *)mhp[SADB_EXT_SA]; - src0 = (struct sadb_address *)(mhp[SADB_EXT_ADDRESS_SRC]); - dst0 = (struct sadb_address *)(mhp[SADB_EXT_ADDRESS_DST]); + sa0 = (struct sadb_sa *)mhp->ext[SADB_EXT_SA]; + src0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_SRC]; + dst0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_DST]; - KEY_SETSECASIDX(proto, msg0->sadb_msg_mode, src0+1, dst0+1, &saidx); + /* XXX boundary check against sa_len */ + KEY_SETSECASIDX(proto, mode, reqid, src0 + 1, dst0 + 1, &saidx); /* get a SA header */ if ((newsah = key_getsah(&saidx)) == NULL) { - /* create a new SA header */ if ((newsah = key_newsah(&saidx)) == NULL) { +#ifdef IPSEC_DEBUG printf("key_add: No more memory.\n"); - msg0->sadb_msg_errno = ENOBUFS; - return NULL; +#endif + return key_senderror(so, m, ENOBUFS); } } + /* set spidx if there */ + /* XXX rewrite */ + error = key_setident(newsah, m, mhp); + if (error) { + return key_senderror(so, m, error); + } + /* create new SA entry. */ /* We can create new SA only if SPI is differenct. */ if (key_getsavbyspi(newsah, sa0->sadb_sa_spi)) { +#ifdef IPSEC_DEBUG printf("key_add: SA already exists.\n"); - msg0->sadb_msg_errno = EEXIST; - return NULL; +#endif + return key_senderror(so, m, EEXIST); } - if ((newsav = key_newsav(mhp, newsah)) == NULL) - return NULL; + newsav = key_newsav(m, mhp, newsah, &error); + if (newsav == NULL) { + return key_senderror(so, m, error); + } /* check SA values to be mature. */ - if ((msg0->sadb_msg_errno = key_mature(newsav)) != NULL) { + if ((error = key_mature(newsav)) != 0) { key_freesav(newsav); - return NULL; + return key_senderror(so, m, error); } /* * don't call key_freesav() here, as we would like to keep the SA * in the database on success. */ { - struct sadb_msg *newmsg; + struct mbuf *n; /* set msg buf from mhp */ - if ((newmsg = key_getmsgbuf_x1(mhp)) == NULL) { - printf("key_add: No more memory.\n"); - msg0->sadb_msg_errno = ENOBUFS; - return NULL; + n = key_getmsgbuf_x1(m, mhp); + if (n == NULL) { +#ifdef IPSEC_DEBUG + printf("key_update: No more memory.\n"); +#endif + return key_senderror(so, m, ENOBUFS); } - return newmsg; + m_freem(m); + return key_sendup_mbuf(so, n, KEY_SENDUP_ALL); } } -static struct sadb_msg * -key_getmsgbuf_x1(mhp) - caddr_t *mhp; +/* m is retained */ +static int +key_setident(sah, m, mhp) + struct secashead *sah; + struct mbuf *m; + const struct sadb_msghdr *mhp; { - struct sadb_msg *msg0; - struct sadb_msg *newmsg; - u_int len; - caddr_t p; + const struct sadb_ident *idsrc, *iddst; + int idsrclen, iddstlen; /* sanity check */ - if (mhp == NULL || mhp[0] == NULL) - panic("key_getmsgbuf_x1: NULL pointer is passed.\n"); + if (sah == NULL || m == NULL || mhp == NULL || mhp->msg == NULL) + panic("key_setident: NULL pointer is passed.\n"); - msg0 = (struct sadb_msg *)mhp[0]; + /* don't make buffer if not there */ + if (mhp->ext[SADB_EXT_IDENTITY_SRC] == NULL && + mhp->ext[SADB_EXT_IDENTITY_DST] == NULL) { + sah->idents = NULL; + sah->identd = NULL; + return 0; + } + + if (mhp->ext[SADB_EXT_IDENTITY_SRC] == NULL || + mhp->ext[SADB_EXT_IDENTITY_DST] == NULL) { +#ifdef IPSEC_DEBUG + printf("key_setident: invalid identity.\n"); +#endif + return EINVAL; + } - /* create new sadb_msg to reply. */ - len = sizeof(struct sadb_msg) - + sizeof(struct sadb_sa) - + PFKEY_EXTLEN(mhp[SADB_EXT_ADDRESS_SRC]) - + PFKEY_EXTLEN(mhp[SADB_EXT_ADDRESS_DST]) - + (mhp[SADB_EXT_LIFETIME_HARD] == NULL - ? 0 : sizeof(struct sadb_lifetime)) - + (mhp[SADB_EXT_LIFETIME_SOFT] == NULL - ? 0 : sizeof(struct sadb_lifetime)); + idsrc = (const struct sadb_ident *)mhp->ext[SADB_EXT_IDENTITY_SRC]; + iddst = (const struct sadb_ident *)mhp->ext[SADB_EXT_IDENTITY_DST]; + idsrclen = mhp->extlen[SADB_EXT_IDENTITY_SRC]; + iddstlen = mhp->extlen[SADB_EXT_IDENTITY_DST]; - KMALLOC(newmsg, struct sadb_msg *, len); - if (newmsg == NULL) - return NULL; - bzero((caddr_t)newmsg, len); + /* validity check */ + if (idsrc->sadb_ident_type != iddst->sadb_ident_type) { +#ifdef IPSEC_DEBUG + printf("key_setident: ident type mismatch.\n"); +#endif + return EINVAL; + } - bcopy((caddr_t)mhp[0], (caddr_t)newmsg, sizeof(*msg0)); - newmsg->sadb_msg_errno = 0; - newmsg->sadb_msg_len = PFKEY_UNIT64(len); - p = (caddr_t)newmsg + sizeof(*msg0); + switch (idsrc->sadb_ident_type) { + case SADB_IDENTTYPE_PREFIX: + case SADB_IDENTTYPE_FQDN: + case SADB_IDENTTYPE_USERFQDN: + default: + /* XXX do nothing */ + sah->idents = NULL; + sah->identd = NULL; + return 0; + } - p = key_setsadbext(p, mhp[SADB_EXT_SA]); - p = key_setsadbext(p, mhp[SADB_EXT_ADDRESS_SRC]); - p = key_setsadbext(p, mhp[SADB_EXT_ADDRESS_DST]); + /* make structure */ + KMALLOC(sah->idents, struct sadb_ident *, idsrclen); + if (sah->idents == NULL) { +#ifdef IPSEC_DEBUG + printf("key_setident: No more memory.\n"); +#endif + return ENOBUFS; + } + KMALLOC(sah->identd, struct sadb_ident *, iddstlen); + if (sah->identd == NULL) { + KFREE(sah->idents); +#ifdef IPSEC_DEBUG + printf("key_setident: No more memory.\n"); +#endif + return ENOBUFS; + } + bcopy(idsrc, sah->idents, idsrclen); + bcopy(iddst, sah->identd, iddstlen); - if (mhp[SADB_EXT_LIFETIME_HARD] != NULL) - p = key_setsadbext(p, mhp[SADB_EXT_LIFETIME_HARD]); + return 0; +} - if (mhp[SADB_EXT_LIFETIME_SOFT] != NULL) - p = key_setsadbext(p, mhp[SADB_EXT_LIFETIME_SOFT]); +/* + * m will not be freed on return. + * it is caller's responsibility to free the result. + */ +static struct mbuf * +key_getmsgbuf_x1(m, mhp) + struct mbuf *m; + const struct sadb_msghdr *mhp; +{ + struct mbuf *n; - return newmsg; + /* sanity check */ + if (m == NULL || mhp == NULL || mhp->msg == NULL) + panic("key_getmsgbuf_x1: NULL pointer is passed.\n"); + + /* create new sadb_msg to reply. */ + n = key_gather_mbuf(m, mhp, 1, 9, SADB_EXT_RESERVED, + SADB_EXT_SA, SADB_X_EXT_SA2, + SADB_EXT_ADDRESS_SRC, SADB_EXT_ADDRESS_DST, + SADB_EXT_LIFETIME_HARD, SADB_EXT_LIFETIME_SOFT, + SADB_EXT_IDENTITY_SRC, SADB_EXT_IDENTITY_DST); + if (!n) + return NULL; + + if (n->m_len < sizeof(struct sadb_msg)) { + n = m_pullup(n, sizeof(struct sadb_msg)); + if (n == NULL) + return NULL; + } + mtod(n, struct sadb_msg *)->sadb_msg_errno = 0; + mtod(n, struct sadb_msg *)->sadb_msg_len = + PFKEY_UNIT64(n->m_pkthdr.len); + + return n; } /* * SADB_DELETE processing * receive * * from the ikmpd, and set SADB_SASTATE_DEAD, * and send, * * to the ikmpd. * - * IN: mhp: pointer to the pointer to each header. - * OUT: NULL if fail. - * other if success, return pointer to the message to send. + * m will always be freed. */ -static struct sadb_msg * -key_delete(mhp) - caddr_t *mhp; +static int +key_delete(so, m, mhp) + struct socket *so; + struct mbuf *m; + const struct sadb_msghdr *mhp; { - struct sadb_msg *msg0; struct sadb_sa *sa0; struct sadb_address *src0, *dst0; struct secasindex saidx; struct secashead *sah; - struct secasvar *sav; + struct secasvar *sav = NULL; u_int16_t proto; /* sanity check */ - if (mhp == NULL || mhp[0] == NULL) + if (so == NULL || m == NULL || mhp == NULL || mhp->msg == NULL) panic("key_delete: NULL pointer is passed.\n"); - msg0 = (struct sadb_msg *)mhp[0]; - /* map satype to proto */ - if ((proto = key_satype2proto(msg0->sadb_msg_satype)) == 0) { + if ((proto = key_satype2proto(mhp->msg->sadb_msg_satype)) == 0) { +#ifdef IPSEC_DEBUG printf("key_delete: invalid satype is passed.\n"); - msg0->sadb_msg_errno = EINVAL; - return NULL; +#endif + return key_senderror(so, m, EINVAL); } - if (mhp[SADB_EXT_SA] == NULL - || mhp[SADB_EXT_ADDRESS_SRC] == NULL - || mhp[SADB_EXT_ADDRESS_DST] == NULL) { + if (mhp->ext[SADB_EXT_SA] == NULL || + mhp->ext[SADB_EXT_ADDRESS_SRC] == NULL || + mhp->ext[SADB_EXT_ADDRESS_DST] == NULL) { +#ifdef IPSEC_DEBUG printf("key_delete: invalid message is passed.\n"); - msg0->sadb_msg_errno = EINVAL; - return NULL; +#endif + return key_senderror(so, m, EINVAL); } - sa0 = (struct sadb_sa *)mhp[SADB_EXT_SA]; - src0 = (struct sadb_address *)(mhp[SADB_EXT_ADDRESS_SRC]); - dst0 = (struct sadb_address *)(mhp[SADB_EXT_ADDRESS_DST]); + if (mhp->extlen[SADB_EXT_SA] < sizeof(struct sadb_sa) || + mhp->extlen[SADB_EXT_ADDRESS_SRC] < sizeof(struct sadb_address) || + mhp->extlen[SADB_EXT_ADDRESS_DST] < sizeof(struct sadb_address)) { +#ifdef IPSEC_DEBUG + printf("key_delete: invalid message is passed.\n"); +#endif + return key_senderror(so, m, EINVAL); + } - KEY_SETSECASIDX(proto, msg0->sadb_msg_mode, src0+1, dst0+1, &saidx); + sa0 = (struct sadb_sa *)mhp->ext[SADB_EXT_SA]; + src0 = (struct sadb_address *)(mhp->ext[SADB_EXT_ADDRESS_SRC]); + dst0 = (struct sadb_address *)(mhp->ext[SADB_EXT_ADDRESS_DST]); + /* XXX boundary check against sa_len */ + KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, &saidx); + /* get a SA header */ - if ((sah = key_getsah(&saidx)) == NULL) { + LIST_FOREACH(sah, &sahtree, chain) { + if (sah->state == SADB_SASTATE_DEAD) + continue; + if (key_cmpsaidx_withoutmode(&sah->saidx, &saidx) == 0) + continue; + + /* get a SA with SPI. */ + sav = key_getsavbyspi(sah, sa0->sadb_sa_spi); + if (sav) + break; + } + if (sah == NULL) { +#ifdef IPSEC_DEBUG printf("key_delete: no SA found.\n"); - msg0->sadb_msg_errno = ENOENT; - return NULL; +#endif + return key_senderror(so, m, ENOENT); } - /* get a SA with SPI. */ - sav = key_getsavbyspi(sah, sa0->sadb_sa_spi); - if (sav == NULL) { - printf("key_delete: no alive SA found.\n"); - msg0->sadb_msg_errno = ENOENT; - return NULL; - } - key_sa_chgstate(sav, SADB_SASTATE_DEAD); key_freesav(sav); sav = NULL; { + struct mbuf *n; struct sadb_msg *newmsg; - u_int len; - caddr_t p; /* create new sadb_msg to reply. */ - len = sizeof(struct sadb_msg) - + sizeof(struct sadb_sa) - + PFKEY_EXTLEN(mhp[SADB_EXT_ADDRESS_SRC]) - + PFKEY_EXTLEN(mhp[SADB_EXT_ADDRESS_DST]); + n = key_gather_mbuf(m, mhp, 1, 4, SADB_EXT_RESERVED, + SADB_EXT_SA, SADB_EXT_ADDRESS_SRC, SADB_EXT_ADDRESS_DST); + if (!n) + return key_senderror(so, m, ENOBUFS); - KMALLOC(newmsg, struct sadb_msg *, len); - if (newmsg == NULL) { - printf("key_delete: No more memory.\n"); - msg0->sadb_msg_errno = ENOBUFS; - return NULL; + if (n->m_len < sizeof(struct sadb_msg)) { + n = m_pullup(n, sizeof(struct sadb_msg)); + if (n == NULL) + return key_senderror(so, m, ENOBUFS); } - bzero((caddr_t)newmsg, len); - - bcopy((caddr_t)mhp[0], (caddr_t)newmsg, sizeof(*msg0)); + newmsg = mtod(n, struct sadb_msg *); newmsg->sadb_msg_errno = 0; - newmsg->sadb_msg_len = PFKEY_UNIT64(len); - p = (caddr_t)newmsg + sizeof(*msg0); + newmsg->sadb_msg_len = PFKEY_UNIT64(n->m_pkthdr.len); - p = key_setsadbext(p, mhp[SADB_EXT_SA]); - p = key_setsadbext(p, mhp[SADB_EXT_ADDRESS_SRC]); - p = key_setsadbext(p, mhp[SADB_EXT_ADDRESS_DST]); - - return newmsg; + m_freem(m); + return key_sendup_mbuf(so, n, KEY_SENDUP_ALL); } } /* * SADB_GET processing * receive * * from the ikmpd, and get a SP and a SA to respond, * and send, * * to the ikmpd. * - * IN: mhp: pointer to the pointer to each header. - * OUT: NULL if fail. - * other if success, return pointer to the message to send. + * m will always be freed. */ -static struct sadb_msg * -key_get(mhp) - caddr_t *mhp; +static int +key_get(so, m, mhp) + struct socket *so; + struct mbuf *m; + const struct sadb_msghdr *mhp; { - struct sadb_msg *msg0; struct sadb_sa *sa0; struct sadb_address *src0, *dst0; struct secasindex saidx; struct secashead *sah; - struct secasvar *sav; + struct secasvar *sav = NULL; u_int16_t proto; /* sanity check */ - if (mhp == NULL || mhp[0] == NULL) + if (so == NULL || m == NULL || mhp == NULL || mhp->msg == NULL) panic("key_get: NULL pointer is passed.\n"); - msg0 = (struct sadb_msg *)mhp[0]; - /* map satype to proto */ - if ((proto = key_satype2proto(msg0->sadb_msg_satype)) == 0) { + if ((proto = key_satype2proto(mhp->msg->sadb_msg_satype)) == 0) { +#ifdef IPSEC_DEBUG printf("key_get: invalid satype is passed.\n"); - msg0->sadb_msg_errno = EINVAL; - return NULL; +#endif + return key_senderror(so, m, EINVAL); } - if (mhp[SADB_EXT_SA] == NULL - || mhp[SADB_EXT_ADDRESS_SRC] == NULL - || mhp[SADB_EXT_ADDRESS_DST] == NULL) { + if (mhp->ext[SADB_EXT_SA] == NULL || + mhp->ext[SADB_EXT_ADDRESS_SRC] == NULL || + mhp->ext[SADB_EXT_ADDRESS_DST] == NULL) { +#ifdef IPSEC_DEBUG printf("key_get: invalid message is passed.\n"); - msg0->sadb_msg_errno = EINVAL; - return NULL; +#endif + return key_senderror(so, m, EINVAL); } - sa0 = (struct sadb_sa *)mhp[SADB_EXT_SA]; - src0 = (struct sadb_address *)(mhp[SADB_EXT_ADDRESS_SRC]); - dst0 = (struct sadb_address *)(mhp[SADB_EXT_ADDRESS_DST]); + if (mhp->extlen[SADB_EXT_SA] < sizeof(struct sadb_sa) || + mhp->extlen[SADB_EXT_ADDRESS_SRC] < sizeof(struct sadb_address) || + mhp->extlen[SADB_EXT_ADDRESS_DST] < sizeof(struct sadb_address)) { +#ifdef IPSEC_DEBUG + printf("key_get: invalid message is passed.\n"); +#endif + return key_senderror(so, m, EINVAL); + } - KEY_SETSECASIDX(proto, msg0->sadb_msg_mode, src0+1, dst0+1, &saidx); + sa0 = (struct sadb_sa *)mhp->ext[SADB_EXT_SA]; + src0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_SRC]; + dst0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_DST]; + /* XXX boundary check against sa_len */ + KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, &saidx); + /* get a SA header */ - if ((sah = key_getsah(&saidx)) == NULL) { + LIST_FOREACH(sah, &sahtree, chain) { + if (sah->state == SADB_SASTATE_DEAD) + continue; + if (key_cmpsaidx_withoutmode(&sah->saidx, &saidx) == 0) + continue; + + /* get a SA with SPI. */ + sav = key_getsavbyspi(sah, sa0->sadb_sa_spi); + if (sav) + break; + } + if (sah == NULL) { +#ifdef IPSEC_DEBUG printf("key_get: no SA found.\n"); - msg0->sadb_msg_errno = ENOENT; - return NULL; +#endif + return key_senderror(so, m, ENOENT); } - /* get a SA with SPI. */ - sav = key_getsavbyspi(sah, sa0->sadb_sa_spi); - if (sav == NULL) { - printf("key_get: no SA with state of mature found.\n"); - msg0->sadb_msg_errno = ENOENT; - return NULL; - } - { - struct sadb_msg *newmsg; - u_int len; + struct mbuf *n; u_int8_t satype; /* map proto to satype */ if ((satype = key_proto2satype(sah->saidx.proto)) == 0) { +#ifdef IPSEC_DEBUG printf("key_get: there was invalid proto in SAD.\n"); - msg0->sadb_msg_errno = EINVAL; - return NULL; +#endif + return key_senderror(so, m, EINVAL); } - /* calculate a length of message buffer */ - len = key_getmsglen(sav); + /* create new sadb_msg to reply. */ + n = key_setdumpsa(sav, SADB_GET, satype, mhp->msg->sadb_msg_seq, + mhp->msg->sadb_msg_pid); + if (!n) + return key_senderror(so, m, ENOBUFS); - KMALLOC(newmsg, struct sadb_msg *, len); - if (newmsg == NULL) { - printf("key_get: No more memory.\n"); - msg0->sadb_msg_errno = ENOBUFS; + m_freem(m); + return key_sendup_mbuf(so, n, KEY_SENDUP_ONE); + } +} + +#ifdef IPSEC_ESP +/* + * XXX reorder combinations by preference + * XXX no idea if the user wants ESP authentication or not + * XXX lifetime - should be in policy? + */ +static struct mbuf * +key_getcomb_esp() +{ + struct sadb_comb *comb; + struct esp_algorithm *algo; + struct mbuf *result = NULL, *m, *n; + int encmin; + int i, off, o; + int totlen; + const int l = PFKEY_ALIGN8(sizeof(struct sadb_comb)); + + m = NULL; + for (i = 1; i < SADB_EALG_MAX; i++) { + algo = &esp_algorithms[i]; + + if (algo->keymax < ipsec_esp_keymin) + continue; + if (algo->keymin < ipsec_esp_keymin) + encmin = ipsec_esp_keymin; + else + encmin = algo->keymin; + + if (ipsec_esp_auth) + m = key_getcomb_ah(); + else { +#ifdef DIAGNOSTIC + if (l > MLEN) + panic("assumption failed in key_getcomb_esp"); +#endif + MGET(m, M_DONTWAIT, MT_DATA); + if (m) { + M_ALIGN(m, l); + m->m_len = l; + m->m_next = NULL; + bzero(mtod(m, caddr_t), m->m_len); + } + } + if (!m) + goto fail; + + totlen = 0; + for (n = m; n; n = n->m_next) + totlen += n->m_len; +#ifdef DIAGNOSTIC + if (totlen % l) + panic("assumption failed in key_getcomb_esp"); +#endif + + for (off = 0; off < totlen; off += l) { + n = m_pulldown(m, off, l, &o); + if (!n) { + /* m is already freed */ + goto fail; + } + comb = (struct sadb_comb *)(mtod(n, caddr_t) + o); + comb->sadb_comb_encrypt = i; + comb->sadb_comb_encrypt_minbits = encmin; + comb->sadb_comb_encrypt_maxbits = algo->keymax; + } + + if (!result) + result = m; + else + m_cat(result, m); + } + + return result; + + fail: + if (result) + m_freem(result); + return NULL; +} +#endif + +/* + * XXX reorder combinations by preference + * XXX lifetime - should be in policy? + */ +static struct mbuf * +key_getcomb_ah() +{ + struct sadb_comb *comb; + struct ah_algorithm *algo; + struct mbuf *m; + int min; + int i; + const int l = PFKEY_ALIGN8(sizeof(struct sadb_comb)); + + m = NULL; + for (i = 1; i < SADB_AALG_MAX; i++) { +#if 1 + /* we prefer HMAC algorithms, not old algorithms */ + if (i != SADB_AALG_SHA1HMAC && i != SADB_AALG_MD5HMAC) + continue; +#endif + algo = &ah_algorithms[i]; + + if (algo->keymax < ipsec_ah_keymin) + continue; + if (algo->keymin < ipsec_ah_keymin) + min = ipsec_ah_keymin; + else + min = algo->keymin; + + if (!m) { +#ifdef DIAGNOSTIC + if (l > MLEN) + panic("assumption failed in key_getcomb_ah"); +#endif + MGET(m, M_DONTWAIT, MT_DATA); + if (m) { + M_ALIGN(m, l); + m->m_len = l; + m->m_next = NULL; + } + } else + M_PREPEND(m, l, M_DONTWAIT); + if (!m) + return NULL; + + comb = mtod(m, struct sadb_comb *); + bzero(comb, sizeof(*comb)); + comb->sadb_comb_auth = i; + comb->sadb_comb_auth_minbits = min; + comb->sadb_comb_auth_maxbits = algo->keymax; + } + + return m; +} + +/* + * XXX no way to pass mode (transport/tunnel) to userland + * XXX replay checking? + * XXX sysctl interface to ipsec_{ah,esp}_keymin + */ +static struct mbuf * +key_getprop(saidx) + const struct secasindex *saidx; +{ + struct sadb_prop *prop; + struct mbuf *m, *n; + const int l = PFKEY_ALIGN8(sizeof(struct sadb_prop)); + int totlen; + + switch (saidx->proto) { +#ifdef IPSEC_ESP + case IPPROTO_ESP: + m = key_getcomb_esp(); + break; +#endif + case IPPROTO_AH: + m = key_getcomb_ah(); + break; + default: return NULL; } - /* create new sadb_msg to reply. */ - (void)key_setdumpsa(newmsg, sav, SADB_GET, - satype, msg0->sadb_msg_seq, msg0->sadb_msg_pid); + if (!m) + return NULL; + M_PREPEND(m, l, M_DONTWAIT); + if (!m) + return NULL; - return newmsg; - } + totlen = 0; + for (n = m; n; n = n->m_next) + totlen += n->m_len; + + prop = mtod(m, struct sadb_prop *); + bzero(prop, sizeof(*prop)); + prop->sadb_prop_len = PFKEY_UNIT64(totlen); + prop->sadb_prop_exttype = SADB_EXT_PROPOSAL; + prop->sadb_prop_replay = 32; /* XXX */ + + return m; } /* * SADB_ACQUIRE processing called by key_checkrequest() and key_acquire2(). * send * * to KMD, and expect to receive * with SADB_ACQUIRE if error occured, * or * with SADB_GETSPI * from KMD by PF_KEY. * * sensitivity is not supported. * * OUT: * 0 : succeed * others: error number */ static int -key_acquire(saidx, spidx) +key_acquire(saidx, sp) struct secasindex *saidx; - struct secpolicyindex *spidx; + struct secpolicy *sp; { + struct mbuf *result = NULL, *m; #ifndef IPSEC_NONBLOCK_ACQUIRE struct secacq *newacq; #endif + struct secpolicyindex *spidx = NULL; u_int8_t satype; - int error; + int error = -1; + u_int32_t seq; /* sanity check */ - if (saidx == NULL || spidx == NULL) + if (saidx == NULL || sp == NULL) panic("key_acquire: NULL pointer is passed.\n"); if ((satype = key_proto2satype(saidx->proto)) == 0) panic("key_acquire: invalid proto is passed.\n"); + spidx = &sp->spidx; + #ifndef IPSEC_NONBLOCK_ACQUIRE /* * We never do anything about acquirng SA. There is anather * solution that kernel blocks to send SADB_ACQUIRE message until * getting something message from IKEd. In later case, to be * managed with ACQUIRING list. */ /* get a entry to check whether sending message or not. */ if ((newacq = key_getacq(saidx)) != NULL) { if (key_blockacq_count < newacq->count) { /* reset counter and do send message. */ newacq->count = 0; } else { /* increment counter and do nothing. */ newacq->count++; return 0; } } else { /* make new entry for blocking to send SADB_ACQUIRE. */ if ((newacq = key_newacq(saidx)) == NULL) return ENOBUFS; /* add to acqtree */ LIST_INSERT_HEAD(&acqtree, newacq, chain); } #endif - { - struct sadb_msg *newmsg = NULL; - union sadb_x_ident_id id; - u_int len; - caddr_t p; - /* create new sadb_msg to reply. */ - len = sizeof(struct sadb_msg) - + sizeof(struct sadb_address) - + PFKEY_ALIGN8(saidx->src.ss_len) - + sizeof(struct sadb_address) - + PFKEY_ALIGN8(saidx->dst.ss_len) - + sizeof(struct sadb_ident) - + PFKEY_ALIGN8(spidx->src.ss_len) - + sizeof(struct sadb_ident) - + PFKEY_ALIGN8(spidx->dst.ss_len) - + sizeof(struct sadb_prop) - + sizeof(struct sadb_comb); /* XXX to be multiple */ +#ifndef IPSEC_NONBLOCK_ACQUIRE + seq = newacq->seq; +#else + seq = (acq_seq = (acq_seq == ~0 ? 1 : ++acq_seq)); +#endif + m = key_setsadbmsg(SADB_ACQUIRE, 0, satype, seq, 0, 0); + if (!m) { + error = ENOBUFS; + goto fail; + } + result = m; - KMALLOC(newmsg, struct sadb_msg *, len); - if (newmsg == 0) { - printf("key_acquire: No more memory.\n"); - return ENOBUFS; + /* set sadb_address for saidx's. */ + m = key_setsadbaddr(SADB_EXT_ADDRESS_SRC, + (struct sockaddr *)&saidx->src, saidx->src.ss_len << 3, + IPSEC_ULPROTO_ANY); + if (!m) { + error = ENOBUFS; + goto fail; } - bzero((caddr_t)newmsg, len); + m_cat(result, m); - newmsg->sadb_msg_version = PF_KEY_V2; - newmsg->sadb_msg_type = SADB_ACQUIRE; - newmsg->sadb_msg_errno = 0; - newmsg->sadb_msg_satype = satype; - newmsg->sadb_msg_len = PFKEY_UNIT64(len); + m = key_setsadbaddr(SADB_EXT_ADDRESS_DST, + (struct sockaddr *)&saidx->dst, saidx->dst.ss_len << 3, + IPSEC_ULPROTO_ANY); + if (!m) { + error = ENOBUFS; + goto fail; + } + m_cat(result, m); -#ifndef IPSEC_NONBLOCK_ACQUIRE - newmsg->sadb_msg_seq = newacq->seq; -#else - newmsg->sadb_msg_seq = (acq_seq = (acq_seq == ~0 ? 1 : ++acq_seq)); -#endif + /* XXX proxy address (optional) */ - newmsg->sadb_msg_pid = 0; - p = (caddr_t)newmsg + sizeof(struct sadb_msg); + /* set sadb_x_policy */ + m = key_setsadbxpolicy(sp->policy, sp->spidx.dir, sp->id); + if (!m) { + error = ENOBUFS; + goto fail; + } + m_cat(result, m); - /* set sadb_address for saidx's. */ - p = key_setsadbaddr(p, - SADB_EXT_ADDRESS_SRC, - (struct sockaddr *)&saidx->src, - _INALENBYAF(saidx->src.ss_family) << 3, - IPSEC_ULPROTO_ANY); - p = key_setsadbaddr(p, - SADB_EXT_ADDRESS_DST, - (struct sockaddr *)&saidx->dst, - _INALENBYAF(saidx->dst.ss_family) << 3, - IPSEC_ULPROTO_ANY); + /* XXX identity (optional) */ +#if 0 + if (idexttype && fqdn) { + /* create identity extension (FQDN) */ + struct sadb_ident *id; + int fqdnlen; - /* set sadb_address for spidx's. */ - id.sadb_x_ident_id_addr.prefix = spidx->prefs; - id.sadb_x_ident_id_addr.ul_proto = spidx->ul_proto; - p = key_setsadbident(p, - SADB_EXT_IDENTITY_SRC, - SADB_X_IDENTTYPE_ADDR, - (caddr_t)&spidx->src, - spidx->src.ss_len, - *(u_int64_t *)&id); + fqdnlen = strlen(fqdn) + 1; /* +1 for terminating-NUL */ + id = (struct sadb_ident *)p; + bzero(id, sizeof(*id) + PFKEY_ALIGN8(fqdnlen)); + id->sadb_ident_len = PFKEY_UNIT64(sizeof(*id) + PFKEY_ALIGN8(fqdnlen)); + id->sadb_ident_exttype = idexttype; + id->sadb_ident_type = SADB_IDENTTYPE_FQDN; + bcopy(fqdn, id + 1, fqdnlen); + p += sizeof(struct sadb_ident) + PFKEY_ALIGN8(fqdnlen); + } - id.sadb_x_ident_id_addr.prefix = spidx->prefd; - id.sadb_x_ident_id_addr.ul_proto = spidx->ul_proto; - p = key_setsadbident(p, - SADB_EXT_IDENTITY_DST, - SADB_X_IDENTTYPE_ADDR, - (caddr_t)&spidx->dst, - spidx->dst.ss_len, - *(u_int64_t *)&id); + if (idexttype) { + /* create identity extension (USERFQDN) */ + struct sadb_ident *id; + int userfqdnlen; - /* create proposal extension */ - /* set combination extension */ - /* XXX: to be defined by proposal database */ - { - struct sadb_prop *prop; - struct sadb_comb *comb; + if (userfqdn) { + /* +1 for terminating-NUL */ + userfqdnlen = strlen(userfqdn) + 1; + } else + userfqdnlen = 0; + id = (struct sadb_ident *)p; + bzero(id, sizeof(*id) + PFKEY_ALIGN8(userfqdnlen)); + id->sadb_ident_len = PFKEY_UNIT64(sizeof(*id) + PFKEY_ALIGN8(userfqdnlen)); + id->sadb_ident_exttype = idexttype; + id->sadb_ident_type = SADB_IDENTTYPE_USERFQDN; + /* XXX is it correct? */ + if (curproc && curproc->p_cred) + id->sadb_ident_id = curproc->p_cred->p_ruid; + if (userfqdn && userfqdnlen) + bcopy(userfqdn, id + 1, userfqdnlen); + p += sizeof(struct sadb_ident) + PFKEY_ALIGN8(userfqdnlen); + } +#endif - prop = (struct sadb_prop *)p; - prop->sadb_prop_len = PFKEY_UNIT64(sizeof(*prop) + sizeof(*comb)); - /* XXX to be multiple */ - prop->sadb_prop_exttype = SADB_EXT_PROPOSAL; - prop->sadb_prop_replay = 32; /* XXX be variable ? */ - p += sizeof(struct sadb_prop); + /* XXX sensitivity (optional) */ - comb = (struct sadb_comb *)p; - comb->sadb_comb_auth = SADB_AALG_SHA1HMAC; /* XXX ??? */ - comb->sadb_comb_encrypt = SADB_EALG_DESCBC; /* XXX ??? */ - comb->sadb_comb_flags = 0; - comb->sadb_comb_auth_minbits = 8; /* XXX */ - comb->sadb_comb_auth_maxbits = 1024; /* XXX */ - comb->sadb_comb_encrypt_minbits = 64; /* XXX */ - comb->sadb_comb_encrypt_maxbits = 64; /* XXX */ - comb->sadb_comb_soft_allocations = 0; - comb->sadb_comb_hard_allocations = 0; - comb->sadb_comb_soft_bytes = 0; - comb->sadb_comb_hard_bytes = 0; - comb->sadb_comb_soft_addtime = 0; - comb->sadb_comb_hard_addtime = 0; - comb->sadb_comb_soft_usetime = 0; - comb->sadb_comb_hard_usetime = 0; + /* create proposal/combination extension */ + m = key_getprop(saidx); + if (!m) { + error = ENOBUFS; + goto fail; + } + m_cat(result, m); - p += sizeof(*comb); - } + if ((result->m_flags & M_PKTHDR) == 0) { + error = EINVAL; + goto fail; + } - error = key_sendall(newmsg, len); - if (error != 0) - printf("key_acquire: key_sendall returned %d\n", error); - return error; - } + if (result->m_len < sizeof(struct sadb_msg)) { + result = m_pullup(result, sizeof(struct sadb_msg)); + if (result == NULL) { + error = ENOBUFS; + goto fail; + } + } - return 0; + result->m_pkthdr.len = 0; + for (m = result; m; m = m->m_next) + result->m_pkthdr.len += m->m_len; + + mtod(result, struct sadb_msg *)->sadb_msg_len = + PFKEY_UNIT64(result->m_pkthdr.len); + + return key_sendup_mbuf(NULL, result, KEY_SENDUP_REGISTERED); + + fail: + if (result) + m_freem(result); + return error; } #ifndef IPSEC_NONBLOCK_ACQUIRE static struct secacq * key_newacq(saidx) struct secasindex *saidx; { struct secacq *newacq; /* get new entry */ KMALLOC(newacq, struct secacq *, sizeof(struct secacq)); if (newacq == NULL) { +#ifdef IPSEC_DEBUG printf("key_newacq: No more memory.\n"); +#endif return NULL; } bzero(newacq, sizeof(*newacq)); /* copy secindex */ bcopy(saidx, &newacq->saidx, sizeof(newacq->saidx)); newacq->seq = (acq_seq == ~0 ? 1 : ++acq_seq); newacq->tick = 0; newacq->count = 0; return newacq; } static struct secacq * key_getacq(saidx) struct secasindex *saidx; { struct secacq *acq; - __LIST_FOREACH(acq, &acqtree, chain) { + LIST_FOREACH(acq, &acqtree, chain) { if (key_cmpsaidx_exactly(saidx, &acq->saidx)) return acq; } return NULL; } static struct secacq * key_getacqbyseq(seq) u_int32_t seq; { struct secacq *acq; - __LIST_FOREACH(acq, &acqtree, chain) { + LIST_FOREACH(acq, &acqtree, chain) { if (acq->seq == seq) return acq; } return NULL; } #endif +static struct secspacq * +key_newspacq(spidx) + struct secpolicyindex *spidx; +{ + struct secspacq *acq; + + /* get new entry */ + KMALLOC(acq, struct secspacq *, sizeof(struct secspacq)); + if (acq == NULL) { +#ifdef IPSEC_DEBUG + printf("key_newspacq: No more memory.\n"); +#endif + return NULL; + } + bzero(acq, sizeof(*acq)); + + /* copy secindex */ + bcopy(spidx, &acq->spidx, sizeof(acq->spidx)); + acq->tick = 0; + acq->count = 0; + + return acq; +} + +static struct secspacq * +key_getspacq(spidx) + struct secpolicyindex *spidx; +{ + struct secspacq *acq; + + LIST_FOREACH(acq, &spacqtree, chain) { + if (key_cmpspidx_exactly(spidx, &acq->spidx)) + return acq; + } + + return NULL; +} + /* * SADB_ACQUIRE processing, * in first situation, is receiving * * from the ikmpd, and clear sequence of its secasvar entry. * * In second situation, is receiving * * from a user land process, and return * * to the socket. * - * IN: mhp: pointer to the pointer to each header. - * OUT: NULL if fail. - * other if success, return pointer to the message to send. + * m will always e freed. */ -static struct sadb_msg * -key_acquire2(mhp) - caddr_t *mhp; +static int +key_acquire2(so, m, mhp) + struct socket *so; + struct mbuf *m; + const struct sadb_msghdr *mhp; { - struct sadb_msg *msg0; - struct sadb_address *src0, *dst0; + const struct sadb_address *src0, *dst0; struct secasindex saidx; struct secashead *sah; u_int16_t proto; + int error; /* sanity check */ - if (mhp == NULL || mhp[0] == NULL) + if (so == NULL || m == NULL || mhp == NULL || mhp->msg == NULL) panic("key_acquire2: NULL pointer is passed.\n"); - msg0 = (struct sadb_msg *)mhp[0]; - /* * Error message from KMd. * We assume that if error was occured in IKEd, the length of PFKEY * message is equal to the size of sadb_msg structure. - * We return ~0 even if error occured in this function. + * We do not raise error even if error occured in this function. */ - if (msg0->sadb_msg_len == PFKEY_UNIT64(sizeof(struct sadb_msg))) { - + if (mhp->msg->sadb_msg_len == PFKEY_UNIT64(sizeof(struct sadb_msg))) { #ifndef IPSEC_NONBLOCK_ACQUIRE struct secacq *acq; /* check sequence number */ - if (msg0->sadb_msg_seq == 0) { + if (mhp->msg->sadb_msg_seq == 0) { +#ifdef IPSEC_DEBUG printf("key_acquire2: must specify sequence number.\n"); - return (struct sadb_msg *)~0; +#endif + m_freem(m); + return 0; } - if ((acq = key_getacqbyseq(msg0->sadb_msg_seq)) == NULL) { + if ((acq = key_getacqbyseq(mhp->msg->sadb_msg_seq)) == NULL) { +#ifdef IPSEC_DEBUG printf("key_acquire2: " "invalid sequence number is passed.\n"); - return (struct sadb_msg *)~0; +#endif + m_freem(m); + return 0; } /* reset acq counter in order to deletion by timehander. */ acq->tick = key_blockacq_lifetime; acq->count = 0; #endif - return (struct sadb_msg *)~0; - /* NOTREACHED */ + m_freem(m); + return 0; } /* * This message is from user land. */ /* map satype to proto */ - if ((proto = key_satype2proto(msg0->sadb_msg_satype)) == 0) { + if ((proto = key_satype2proto(mhp->msg->sadb_msg_satype)) == 0) { +#ifdef IPSEC_DEBUG printf("key_acquire2: invalid satype is passed.\n"); - msg0->sadb_msg_errno = EINVAL; - return NULL; +#endif + return key_senderror(so, m, EINVAL); } - if (mhp[SADB_EXT_ADDRESS_SRC] == NULL - || mhp[SADB_EXT_ADDRESS_DST] == NULL - || mhp[SADB_EXT_PROPOSAL] == NULL) { + if (mhp->ext[SADB_EXT_ADDRESS_SRC] == NULL || + mhp->ext[SADB_EXT_ADDRESS_DST] == NULL || + mhp->ext[SADB_EXT_PROPOSAL] == NULL) { /* error */ +#ifdef IPSEC_DEBUG printf("key_acquire2: invalid message is passed.\n"); - msg0->sadb_msg_errno = EINVAL; - return NULL; +#endif + return key_senderror(so, m, EINVAL); } - src0 = (struct sadb_address *)(mhp[SADB_EXT_ADDRESS_SRC]); - dst0 = (struct sadb_address *)(mhp[SADB_EXT_ADDRESS_DST]); + if (mhp->extlen[SADB_EXT_ADDRESS_SRC] < sizeof(struct sadb_address) || + mhp->extlen[SADB_EXT_ADDRESS_DST] < sizeof(struct sadb_address) || + mhp->extlen[SADB_EXT_PROPOSAL] < sizeof(struct sadb_prop)) { + /* error */ +#ifdef IPSEC_DEBUG + printf("key_acquire2: invalid message is passed.\n"); +#endif + return key_senderror(so, m, EINVAL); + } - KEY_SETSECASIDX(proto, msg0->sadb_msg_mode, src0+1, dst0+1, &saidx); + src0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_SRC]; + dst0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_DST]; + /* XXX boundary check against sa_len */ + KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, &saidx); + /* get a SA index */ - if ((sah = key_getsah(&saidx)) != NULL) { + LIST_FOREACH(sah, &sahtree, chain) { + if (sah->state == SADB_SASTATE_DEAD) + continue; + if (key_cmpsaidx_withmode(&sah->saidx, &saidx)) + break; + } + if (sah != NULL) { +#ifdef IPSEC_DEBUG printf("key_acquire2: a SA exists already.\n"); - msg0->sadb_msg_errno = EEXIST; - return NULL; +#endif + return key_senderror(so, m, EEXIST); } - msg0->sadb_msg_errno = key_acquire(&saidx, NULL); - if (msg0->sadb_msg_errno != 0) { - /* XXX What I do ? */ + error = key_acquire(&saidx, NULL); + if (error != 0) { +#ifdef IPSEC_DEBUG printf("key_acquire2: error %d returned " - "from key_acquire.\n", msg0->sadb_msg_errno); - return NULL; + "from key_acquire.\n", mhp->msg->sadb_msg_errno); +#endif + return key_senderror(so, m, error); } - { - struct sadb_msg *newmsg; - u_int len; - - /* create new sadb_msg to reply. */ - len = PFKEY_UNUNIT64(msg0->sadb_msg_len); - - KMALLOC(newmsg, struct sadb_msg *, len); - if (newmsg == NULL) { - printf("key_acquire2: No more memory.\n"); - msg0->sadb_msg_errno = ENOBUFS; - return NULL; - } - bzero((caddr_t)newmsg, len); - - bcopy(mhp[0], (caddr_t)newmsg, len); - - return newmsg; - } + return key_sendup_mbuf(so, m, KEY_SENDUP_REGISTERED); } /* * SADB_REGISTER processing. * If SATYPE_UNSPEC has been passed as satype, only return sabd_supported. * receive * * from the ikmpd, and register a socket to send PF_KEY messages, * and send * * to KMD by PF_KEY. * If socket is detached, must free from regnode. - * OUT: - * 0 : succeed - * others: error number + * + * m will always e freed. */ -static struct sadb_msg * -key_register(mhp, so) - caddr_t *mhp; +static int +key_register(so, m, mhp) struct socket *so; + struct mbuf *m; + const struct sadb_msghdr *mhp; { - struct sadb_msg *msg0; struct secreg *reg, *newreg = 0; /* sanity check */ - if (mhp == NULL || so == NULL || mhp[0] == NULL) + if (so == NULL || m == NULL || mhp == NULL || mhp->msg == NULL) panic("key_register: NULL pointer is passed.\n"); - msg0 = (struct sadb_msg *)mhp[0]; + /* check for invalid register message */ + if (mhp->msg->sadb_msg_satype >= sizeof(regtree)/sizeof(regtree[0])) + return key_senderror(so, m, EINVAL); /* When SATYPE_UNSPEC is specified, only return sabd_supported. */ - if (msg0->sadb_msg_satype == SADB_SATYPE_UNSPEC) + if (mhp->msg->sadb_msg_satype == SADB_SATYPE_UNSPEC) goto setmsg; /* check whether existing or not */ - __LIST_FOREACH(reg, ®tree[msg0->sadb_msg_satype], chain) { + LIST_FOREACH(reg, ®tree[mhp->msg->sadb_msg_satype], chain) { if (reg->so == so) { +#ifdef IPSEC_DEBUG printf("key_register: socket exists already.\n"); - msg0->sadb_msg_errno = EEXIST; - return NULL; +#endif + return key_senderror(so, m, EEXIST); } } /* create regnode */ - KMALLOC(newreg, struct secreg *, sizeof(struct secreg)); + KMALLOC(newreg, struct secreg *, sizeof(*newreg)); if (newreg == NULL) { +#ifdef IPSEC_DEBUG printf("key_register: No more memory.\n"); - msg0->sadb_msg_errno = ENOBUFS; - return NULL; +#endif + return key_senderror(so, m, ENOBUFS); } - bzero((caddr_t)newreg, sizeof(struct secreg)); + bzero((caddr_t)newreg, sizeof(*newreg)); newreg->so = so; ((struct keycb *)sotorawcb(so))->kp_registered++; /* add regnode to regtree. */ - LIST_INSERT_HEAD(®tree[msg0->sadb_msg_satype], newreg, chain); + LIST_INSERT_HEAD(®tree[mhp->msg->sadb_msg_satype], newreg, chain); setmsg: - { + { + struct mbuf *n; struct sadb_msg *newmsg; struct sadb_supported *sup; u_int len, alen, elen; - caddr_t p; + int off; + int i; + struct sadb_alg *alg; /* create new sadb_msg to reply. */ alen = sizeof(struct sadb_supported) + ((SADB_AALG_MAX - 1) * sizeof(struct sadb_alg)); - #ifdef IPSEC_ESP elen = sizeof(struct sadb_supported) + ((SADB_EALG_MAX - 1) * sizeof(struct sadb_alg)); #else elen = 0; #endif - len = sizeof(struct sadb_msg) - + alen - + elen; + len = sizeof(struct sadb_msg) + alen + elen; - KMALLOC(newmsg, struct sadb_msg *, len); - if (newmsg == NULL) { - printf("key_register: No more memory.\n"); - msg0->sadb_msg_errno = ENOBUFS; - return NULL; + if (len > MCLBYTES) + return key_senderror(so, m, ENOBUFS); + + MGETHDR(n, M_DONTWAIT, MT_DATA); + if (len > MHLEN) { + MCLGET(n, M_DONTWAIT); + if ((n->m_flags & M_EXT) == 0) { + m_freem(n); + n = NULL; + } } - bzero((caddr_t)newmsg, len); + if (!n) + return key_senderror(so, m, ENOBUFS); - bcopy((caddr_t)mhp[0], (caddr_t)newmsg, sizeof(*msg0)); + n->m_pkthdr.len = n->m_len = len; + n->m_next = NULL; + off = 0; + + m_copydata(m, 0, sizeof(struct sadb_msg), mtod(n, caddr_t) + off); + newmsg = mtod(n, struct sadb_msg *); newmsg->sadb_msg_errno = 0; newmsg->sadb_msg_len = PFKEY_UNIT64(len); - p = (caddr_t)newmsg + sizeof(*msg0); + off += PFKEY_ALIGN8(sizeof(struct sadb_msg)); /* for authentication algorithm */ - sup = (struct sadb_supported *)p; - sup->sadb_supported_len = PFKEY_UNIT64(alen); - sup->sadb_supported_exttype = SADB_EXT_SUPPORTED_AUTH; - p += sizeof(*sup); + if (alen) { + sup = (struct sadb_supported *)(mtod(n, caddr_t) + off); + sup->sadb_supported_len = PFKEY_UNIT64(alen); + sup->sadb_supported_exttype = SADB_EXT_SUPPORTED_AUTH; + off += PFKEY_ALIGN8(sizeof(*sup)); - { - int i; - struct sadb_alg *alg; - struct ah_algorithm *algo; + for (i = 1; i < SADB_AALG_MAX; i++) { + struct ah_algorithm *aalgo; - for (i = 1; i < SADB_AALG_MAX; i++) { - algo = &ah_algorithms[i]; - alg = (struct sadb_alg *)p; - alg->sadb_alg_id = i; - alg->sadb_alg_ivlen = 0; - alg->sadb_alg_minbits = algo->keymin; - alg->sadb_alg_maxbits = algo->keymax; - p += sizeof(struct sadb_alg); + aalgo = &ah_algorithms[i]; + alg = (struct sadb_alg *)(mtod(n, caddr_t) + off); + alg->sadb_alg_id = i; + alg->sadb_alg_ivlen = 0; + alg->sadb_alg_minbits = aalgo->keymin; + alg->sadb_alg_maxbits = aalgo->keymax; + off += PFKEY_ALIGN8(sizeof(*alg)); + } } - } #ifdef IPSEC_ESP /* for encryption algorithm */ - sup = (struct sadb_supported *)p; - sup->sadb_supported_len = PFKEY_UNIT64(elen); - sup->sadb_supported_exttype = SADB_EXT_SUPPORTED_ENCRYPT; - p += sizeof(*sup); + if (elen) { + sup = (struct sadb_supported *)(mtod(n, caddr_t) + off); + sup->sadb_supported_len = PFKEY_UNIT64(elen); + sup->sadb_supported_exttype = SADB_EXT_SUPPORTED_ENCRYPT; + off += PFKEY_ALIGN8(sizeof(*sup)); - { - int i; - struct sadb_alg *alg; - struct esp_algorithm *algo; + for (i = 1; i < SADB_EALG_MAX; i++) { + struct esp_algorithm *ealgo; - for (i = 1; i < SADB_EALG_MAX; i++) { - algo = &esp_algorithms[i]; - - alg = (struct sadb_alg *)p; - alg->sadb_alg_id = i; - if (algo && algo->ivlen) { - /* - * give NULL to get the value preferred by algorithm - * XXX SADB_X_EXT_DERIV ? - */ - alg->sadb_alg_ivlen = (*algo->ivlen)(NULL); - } else - alg->sadb_alg_ivlen = 0; - alg->sadb_alg_minbits = algo->keymin; - alg->sadb_alg_maxbits = algo->keymax; - p += sizeof(struct sadb_alg); + ealgo = &esp_algorithms[i]; + alg = (struct sadb_alg *)(mtod(n, caddr_t) + off); + alg->sadb_alg_id = i; + if (ealgo && ealgo->ivlen) { + /* + * give NULL to get the value preferred by + * algorithm XXX SADB_X_EXT_DERIV ? + */ + alg->sadb_alg_ivlen = (*ealgo->ivlen)(NULL); + } else + alg->sadb_alg_ivlen = 0; + alg->sadb_alg_minbits = ealgo->keymin; + alg->sadb_alg_maxbits = ealgo->keymax; + off += PFKEY_ALIGN8(sizeof(struct sadb_alg)); + } } - } #endif - return newmsg; - } +#ifdef DIGAGNOSTIC + if (off != len) + panic("length assumption failed in key_register"); +#endif + + m_freem(m); + return key_sendup_mbuf(so, n, KEY_SENDUP_REGISTERED); + } } /* * free secreg entry registered. * XXX: I want to do free a socket marked done SADB_RESIGER to socket. */ void key_freereg(so) struct socket *so; { struct secreg *reg; int i; /* sanity check */ if (so == NULL) panic("key_freereg: NULL pointer is passed.\n"); /* * check whether existing or not. * check all type of SA, because there is a potential that * one socket is registered to multiple type of SA. */ for (i = 0; i <= SADB_SATYPE_MAX; i++) { - __LIST_FOREACH(reg, ®tree[i], chain) { + LIST_FOREACH(reg, ®tree[i], chain) { if (reg->so == so && __LIST_CHAINED(reg)) { LIST_REMOVE(reg, chain); KFREE(reg); break; } } } - + return; } /* * SADB_EXPIRE processing * send - * + * * to KMD by PF_KEY. * NOTE: We send only soft lifetime extension. * * OUT: 0 : succeed * others : error number */ static int key_expire(sav) struct secasvar *sav; { int s; int satype; + struct mbuf *result = NULL, *m; + int len; + int error = -1; + struct sadb_lifetime *lt; /* XXX: Why do we lock ? */ s = splnet(); /*called from softclock()*/ /* sanity check */ if (sav == NULL) panic("key_expire: NULL pointer is passed.\n"); if (sav->sah == NULL) panic("key_expire: Why was SA index in SA NULL.\n"); if ((satype = key_proto2satype(sav->sah->saidx.proto)) == 0) panic("key_expire: invalid proto is passed.\n"); - { - struct sadb_msg *newmsg = NULL; - u_int len; - caddr_t p; - int error; + /* set msg header */ + m = key_setsadbmsg(SADB_EXPIRE, 0, satype, sav->seq, 0, sav->refcnt); + if (!m) { + error = ENOBUFS; + goto fail; + } + result = m; - /* create new sadb_msg to reply. */ - len = sizeof(struct sadb_msg) - + sizeof(struct sadb_sa) - + sizeof(struct sadb_lifetime) - + sizeof(struct sadb_lifetime) - + sizeof(struct sadb_address) - + PFKEY_ALIGN8(sav->sah->saidx.src.ss_len) - + sizeof(struct sadb_address) - + PFKEY_ALIGN8(sav->sah->saidx.dst.ss_len); + /* create SA extension */ + m = key_setsadbsa(sav); + if (!m) { + error = ENOBUFS; + goto fail; + } + m_cat(result, m); - KMALLOC(newmsg, struct sadb_msg *, len); - if (newmsg == NULL) { - printf("key_expire: No more memory.\n"); - splx(s); - return ENOBUFS; + /* create SA extension */ + m = key_setsadbxsa2(sav->sah->saidx.mode, sav->sah->saidx.reqid); + if (!m) { + error = ENOBUFS; + goto fail; } - bzero((caddr_t)newmsg, len); + m_cat(result, m); - /* set msg header */ - p = key_setsadbmsg((caddr_t)newmsg, SADB_EXPIRE, len, - satype, sav->seq, 0, - sav->sah->saidx.mode, sav->refcnt); + /* create lifetime extension (current and soft) */ + len = PFKEY_ALIGN8(sizeof(*lt)) * 2; + m = key_alloc_mbuf(len); + if (!m || m->m_next) { /*XXX*/ + if (m) + m_freem(m); + error = ENOBUFS; + goto fail; + } + bzero(mtod(m, caddr_t), len); + lt = mtod(m, struct sadb_lifetime *); + lt->sadb_lifetime_len = PFKEY_UNIT64(sizeof(struct sadb_lifetime)); + lt->sadb_lifetime_exttype = SADB_EXT_LIFETIME_CURRENT; + lt->sadb_lifetime_allocations = sav->lft_c->sadb_lifetime_allocations; + lt->sadb_lifetime_bytes = sav->lft_c->sadb_lifetime_bytes; + lt->sadb_lifetime_addtime = sav->lft_c->sadb_lifetime_addtime; + lt->sadb_lifetime_usetime = sav->lft_c->sadb_lifetime_usetime; + lt = (struct sadb_lifetime *)(mtod(m, caddr_t) + len / 2); + bcopy(sav->lft_s, lt, sizeof(*lt)); + m_cat(result, m); - /* create SA extension */ - p = key_setsadbsa(p, sav); + /* set sadb_address for source */ + m = key_setsadbaddr(SADB_EXT_ADDRESS_SRC, + (struct sockaddr *)&sav->sah->saidx.src, + sav->sah->saidx.src.ss_len << 3, IPSEC_ULPROTO_ANY); + if (!m) { + error = ENOBUFS; + goto fail; + } + m_cat(result, m); - /* create lifetime extension */ - { - struct sadb_lifetime *m_lt = (struct sadb_lifetime *)p; + /* set sadb_address for destination */ + m = key_setsadbaddr(SADB_EXT_ADDRESS_DST, + (struct sockaddr *)&sav->sah->saidx.dst, + sav->sah->saidx.dst.ss_len << 3, IPSEC_ULPROTO_ANY); + if (!m) { + error = ENOBUFS; + goto fail; + } + m_cat(result, m); - m_lt->sadb_lifetime_len = PFKEY_UNIT64(sizeof(struct sadb_lifetime)); - m_lt->sadb_lifetime_exttype = SADB_EXT_LIFETIME_CURRENT; - m_lt->sadb_lifetime_allocations = sav->lft_c->sadb_lifetime_allocations; - m_lt->sadb_lifetime_bytes = sav->lft_c->sadb_lifetime_bytes; - m_lt->sadb_lifetime_addtime = sav->lft_c->sadb_lifetime_addtime; - m_lt->sadb_lifetime_usetime = sav->lft_c->sadb_lifetime_usetime; - p += sizeof(struct sadb_lifetime); + if ((result->m_flags & M_PKTHDR) == 0) + goto fail; - /* copy SOFT lifetime extension. */ - bcopy(sav->lft_s, p, sizeof(struct sadb_lifetime)); - p += sizeof(struct sadb_lifetime); - } + if (result->m_len < sizeof(struct sadb_msg)) { + result = m_pullup(result, sizeof(struct sadb_msg)); + if (result == NULL) + goto fail; + } - /* set sadb_address for source */ - p = key_setsadbaddr(p, - SADB_EXT_ADDRESS_SRC, - (struct sockaddr *)&sav->sah->saidx.src, - _INALENBYAF(sav->sah->saidx.src.ss_family) << 3, - IPSEC_ULPROTO_ANY); + result->m_pkthdr.len = 0; + for (m = result; m; m = m->m_next) + result->m_pkthdr.len += m->m_len; - /* set sadb_address for destination */ - p = key_setsadbaddr(p, - SADB_EXT_ADDRESS_DST, - (struct sockaddr *)&sav->sah->saidx.dst, - _INALENBYAF(sav->sah->saidx.dst.ss_family) << 3, - IPSEC_ULPROTO_ANY); + mtod(result, struct sadb_msg *)->sadb_msg_len = + PFKEY_UNIT64(result->m_pkthdr.len); - error = key_sendall(newmsg, len); + return key_sendup_mbuf(NULL, result, KEY_SENDUP_REGISTERED); + + fail: + if (result) + m_freem(result); splx(s); return error; - } } /* * SADB_FLUSH processing * receive * * from the ikmpd, and free all entries in secastree. * and send, * * to the ikmpd. * NOTE: to do is only marking SADB_SASTATE_DEAD. * - * IN: mhp: pointer to the pointer to each header. - * OUT: NULL if fail. - * other if success, return pointer to the message to send. + * m will always be freed. */ -static struct sadb_msg * -key_flush(mhp) - caddr_t *mhp; +static int +key_flush(so, m, mhp) + struct socket *so; + struct mbuf *m; + const struct sadb_msghdr *mhp; { - struct sadb_msg *msg0; + struct sadb_msg *newmsg; struct secashead *sah, *nextsah; struct secasvar *sav, *nextsav; u_int16_t proto; u_int8_t state; u_int stateidx; /* sanity check */ - if (mhp == NULL || mhp[0] == NULL) + if (so == NULL || mhp == NULL || mhp->msg == NULL) panic("key_flush: NULL pointer is passed.\n"); - msg0 = (struct sadb_msg *)mhp[0]; - /* map satype to proto */ - if ((proto = key_satype2proto(msg0->sadb_msg_satype)) == 0) { + if ((proto = key_satype2proto(mhp->msg->sadb_msg_satype)) == 0) { +#ifdef IPSEC_DEBUG printf("key_flush: invalid satype is passed.\n"); - msg0->sadb_msg_errno = EINVAL; - return NULL; +#endif + return key_senderror(so, m, EINVAL); } /* no SATYPE specified, i.e. flushing all SA. */ for (sah = LIST_FIRST(&sahtree); sah != NULL; sah = nextsah) { - nextsah = LIST_NEXT(sah, chain); - if (msg0->sadb_msg_satype != SADB_SATYPE_UNSPEC + if (mhp->msg->sadb_msg_satype != SADB_SATYPE_UNSPEC && proto != sah->saidx.proto) continue; for (stateidx = 0; stateidx < _ARRAYLEN(saorder_state_alive); stateidx++) { - state = saorder_state_any[stateidx]; for (sav = LIST_FIRST(&sah->savtree[state]); sav != NULL; sav = nextsav) { nextsav = LIST_NEXT(sav, chain); key_sa_chgstate(sav, SADB_SASTATE_DEAD); + key_freesav(sav); } } sah->state = SADB_SASTATE_DEAD; } - { - struct sadb_msg *newmsg; - u_int len; - - /* create new sadb_msg to reply. */ - len = sizeof(struct sadb_msg); - - KMALLOC(newmsg, struct sadb_msg *, len); - if (newmsg == NULL) { + if (m->m_len < sizeof(struct sadb_msg) || + sizeof(struct sadb_msg) > m->m_len + M_TRAILINGSPACE(m)) { +#ifdef IPSEC_DEBUG printf("key_flush: No more memory.\n"); - msg0->sadb_msg_errno = ENOBUFS; - return NULL; +#endif + return key_senderror(so, m, ENOBUFS); } - bzero((caddr_t)newmsg, len); - bcopy((caddr_t)mhp[0], (caddr_t)newmsg, sizeof(*msg0)); + if (m->m_next) + m_freem(m->m_next); + m->m_next = NULL; + m->m_pkthdr.len = m->m_len = sizeof(struct sadb_msg); + newmsg = mtod(m, struct sadb_msg *); newmsg->sadb_msg_errno = 0; - newmsg->sadb_msg_len = PFKEY_UNIT64(len); + newmsg->sadb_msg_len = PFKEY_UNIT64(m->m_pkthdr.len); - return newmsg; - } + return key_sendup_mbuf(so, m, KEY_SENDUP_ALL); } /* * SADB_DUMP processing * dump all entries including status of DEAD in SAD. * receive * * from the ikmpd, and dump all secasvar leaves * and send, * ..... * to the ikmpd. * - * IN: mhp: pointer to the pointer to each header. - * OUT: error code. 0 on success. + * m will always be freed. */ static int -key_dump(mhp, so, target) - caddr_t *mhp; +key_dump(so, m, mhp) struct socket *so; - int target; + struct mbuf *m; + const struct sadb_msghdr *mhp; { - struct sadb_msg *msg0; struct secashead *sah; struct secasvar *sav; u_int16_t proto; u_int stateidx; u_int8_t satype; u_int8_t state; - int len, cnt; + int cnt; struct sadb_msg *newmsg; + struct mbuf *n; /* sanity check */ - if (mhp == NULL || mhp[0] == NULL) + if (so == NULL || m == NULL || mhp == NULL || mhp->msg == NULL) panic("key_dump: NULL pointer is passed.\n"); - msg0 = (struct sadb_msg *)mhp[0]; - /* map satype to proto */ - if ((proto = key_satype2proto(msg0->sadb_msg_satype)) == 0) { + if ((proto = key_satype2proto(mhp->msg->sadb_msg_satype)) == 0) { +#ifdef IPSEC_DEBUG printf("key_dump: invalid satype is passed.\n"); - msg0->sadb_msg_errno = EINVAL; - return NULL; +#endif + return key_senderror(so, m, EINVAL); } /* count sav entries to be sent to the userland. */ cnt = 0; - __LIST_FOREACH(sah, &sahtree, chain) { - - if (msg0->sadb_msg_satype != SADB_SATYPE_UNSPEC + LIST_FOREACH(sah, &sahtree, chain) { + if (mhp->msg->sadb_msg_satype != SADB_SATYPE_UNSPEC && proto != sah->saidx.proto) continue; for (stateidx = 0; stateidx < _ARRAYLEN(saorder_state_any); stateidx++) { - state = saorder_state_any[stateidx]; - __LIST_FOREACH(sav, &sah->savtree[state], chain) { + LIST_FOREACH(sav, &sah->savtree[state], chain) { cnt++; } } } if (cnt == 0) - return ENOENT; + return key_senderror(so, m, ENOENT); /* send this to the userland, one at a time. */ newmsg = NULL; - __LIST_FOREACH(sah, &sahtree, chain) { - - if (msg0->sadb_msg_satype != SADB_SATYPE_UNSPEC + LIST_FOREACH(sah, &sahtree, chain) { + if (mhp->msg->sadb_msg_satype != SADB_SATYPE_UNSPEC && proto != sah->saidx.proto) continue; /* map proto to satype */ if ((satype = key_proto2satype(sah->saidx.proto)) == 0) { +#ifdef IPSEC_DEBUG printf("key_dump: there was invalid proto in SAD.\n"); - msg0->sadb_msg_errno = EINVAL; - return NULL; +#endif + return key_senderror(so, m, EINVAL); } for (stateidx = 0; stateidx < _ARRAYLEN(saorder_state_any); stateidx++) { - state = saorder_state_any[stateidx]; - __LIST_FOREACH(sav, &sah->savtree[state], chain) { + LIST_FOREACH(sav, &sah->savtree[state], chain) { + n = key_setdumpsa(sav, SADB_DUMP, satype, + --cnt, mhp->msg->sadb_msg_pid); + if (!n) + return key_senderror(so, m, ENOBUFS); - len = key_getmsglen(sav); - KMALLOC(newmsg, struct sadb_msg *, len); - if (newmsg == NULL) { - printf("key_dump: No more memory.\n"); - return ENOBUFS; - } - bzero((caddr_t)newmsg, len); - - --cnt; - (void)key_setdumpsa(newmsg, sav, SADB_DUMP, - satype, cnt, msg0->sadb_msg_pid); - - key_sendup(so, newmsg, len, target); - KFREE(newmsg); - newmsg = NULL; + key_sendup_mbuf(so, n, KEY_SENDUP_ONE); } } } + m_freem(m); return 0; } /* * SADB_X_PROMISC processing + * + * m will always be freed. */ -static void -key_promisc(mhp, so) - caddr_t *mhp; +static int +key_promisc(so, m, mhp) struct socket *so; + struct mbuf *m; + const struct sadb_msghdr *mhp; { - struct sadb_msg *msg0; int olen; /* sanity check */ - if (mhp == NULL || mhp[0] == NULL) + if (so == NULL || m == NULL || mhp == NULL || mhp->msg == NULL) panic("key_promisc: NULL pointer is passed.\n"); - msg0 = (struct sadb_msg *)mhp[0]; - olen = PFKEY_UNUNIT64(msg0->sadb_msg_len); + olen = PFKEY_UNUNIT64(mhp->msg->sadb_msg_len); if (olen < sizeof(struct sadb_msg)) { - return; +#if 1 + return key_senderror(so, m, EINVAL); +#else + m_freem(m); + return 0; +#endif } else if (olen == sizeof(struct sadb_msg)) { /* enable/disable promisc mode */ struct keycb *kp; - int target = 0; - target = KEY_SENDUP_ONE; - - if (so == NULL) { - return; + if ((kp = (struct keycb *)sotorawcb(so)) == NULL) + return key_senderror(so, m, EINVAL); + mhp->msg->sadb_msg_errno = 0; + switch (mhp->msg->sadb_msg_satype) { + case 0: + case 1: + kp->kp_promisc = mhp->msg->sadb_msg_satype; + break; + default: + return key_senderror(so, m, EINVAL); } - if ((kp = (struct keycb *)sotorawcb(so)) == NULL) { - msg0->sadb_msg_errno = EINVAL; - goto sendorig; - } - msg0->sadb_msg_errno = 0; - if (msg0->sadb_msg_satype == 1 || msg0->sadb_msg_satype == 0) { - kp->kp_promisc = msg0->sadb_msg_satype; - } else { - msg0->sadb_msg_errno = EINVAL; - goto sendorig; - } /* send the original message back to everyone */ - msg0->sadb_msg_errno = 0; - target = KEY_SENDUP_ALL; -sendorig: - key_sendup(so, msg0, PFKEY_UNUNIT64(msg0->sadb_msg_len), target); + mhp->msg->sadb_msg_errno = 0; + return key_sendup_mbuf(so, m, KEY_SENDUP_ALL); } else { /* send packet as is */ - struct sadb_msg *msg; - int len; - len = olen - sizeof(struct sadb_msg); - KMALLOC(msg, struct sadb_msg *, len); - if (msg == NULL) { - msg0->sadb_msg_errno = ENOBUFS; - key_sendup(so, msg0, PFKEY_UNUNIT64(msg0->sadb_msg_len), - KEY_SENDUP_ONE); /*XXX*/ - } + m_adj(m, PFKEY_ALIGN8(sizeof(struct sadb_msg))); - /* XXX if sadb_msg_seq is specified, send to specific pid */ - key_sendup(so, msg, len, KEY_SENDUP_ALL); - KFREE(msg); + /* TODO: if sadb_msg_seq is specified, send to specific pid */ + return key_sendup_mbuf(so, m, KEY_SENDUP_ALL); } } -/* - * send message to the socket. - * OUT: - * 0 : success - * others : fail - */ -static int -key_sendall(msg, len) - struct sadb_msg *msg; - u_int len; -{ - struct secreg *reg; - int error = 0; +static int (*key_typesw[]) __P((struct socket *, struct mbuf *, + const struct sadb_msghdr *)) = { + NULL, /* SADB_RESERVED */ + key_getspi, /* SADB_GETSPI */ + key_update, /* SADB_UPDATE */ + key_add, /* SADB_ADD */ + key_delete, /* SADB_DELETE */ + key_get, /* SADB_GET */ + key_acquire2, /* SADB_ACQUIRE */ + key_register, /* SADB_REGISTER */ + NULL, /* SADB_EXPIRE */ + key_flush, /* SADB_FLUSH */ + key_dump, /* SADB_DUMP */ + key_promisc, /* SADB_X_PROMISC */ + NULL, /* SADB_X_PCHANGE */ + key_spdadd, /* SADB_X_SPDUPDATE */ + key_spdadd, /* SADB_X_SPDADD */ + key_spddelete, /* SADB_X_SPDDELETE */ + key_spdget, /* SADB_X_SPDGET */ + NULL, /* SADB_X_SPDACQUIRE */ + key_spddump, /* SADB_X_SPDDUMP */ + key_spdflush, /* SADB_X_SPDFLUSH */ + key_spdadd, /* SADB_X_SPDSETIDX */ + NULL, /* SADB_X_SPDEXPIRE */ + key_spddelete2, /* SADB_X_SPDDELETE2 */ +}; - /* sanity check */ - if (msg == NULL) - panic("key_sendall: NULL pointer is passed.\n"); - - /* search table registerd socket to send a message. */ - __LIST_FOREACH(reg, ®tree[msg->sadb_msg_satype], chain) { - error = key_sendup(reg->so, msg, len, KEY_SENDUP_ONE); - if (error != 0) { - if (error == ENOBUFS) - printf("key_sendall: No more memory.\n"); - else { - printf("key_sendall: key_sendup returned %d\n", - error); - } - KFREE(msg); - return error; - } - } - - KFREE(msg); - return 0; -} - /* * parse sadb_msg buffer to process PFKEYv2, * and create a data to response if needed. * I think to be dealed with mbuf directly. * IN: * msgp : pointer to pointer to a received buffer pulluped. * This is rewrited to response. * so : pointer to socket. * OUT: * length for buffer to send to user process. */ int -key_parse(msgp, so, targetp) - struct sadb_msg **msgp; +key_parse(m, so) + struct mbuf *m; struct socket *so; - int *targetp; { - struct sadb_msg *msg = *msgp, *newmsg = NULL; - caddr_t mhp[SADB_EXT_MAX + 1]; + struct sadb_msg *msg; + struct sadb_msghdr mh; u_int orglen; int error; + int target; /* sanity check */ - if (msg == NULL || so == NULL) + if (m == NULL || so == NULL) panic("key_parse: NULL pointer is passed.\n"); +#if 0 /*kdebug_sadb assumes msg in linear buffer*/ KEYDEBUG(KEYDEBUG_KEY_DUMP, printf("key_parse: passed sadb_msg\n"); kdebug_sadb(msg)); +#endif + if (m->m_len < sizeof(struct sadb_msg)) { + m = m_pullup(m, sizeof(struct sadb_msg)); + if (!m) + return ENOBUFS; + } + msg = mtod(m, struct sadb_msg *); orglen = PFKEY_UNUNIT64(msg->sadb_msg_len); + target = KEY_SENDUP_ONE; - if (targetp) - *targetp = KEY_SENDUP_ONE; + if ((m->m_flags & M_PKTHDR) == 0 || + m->m_pkthdr.len != m->m_pkthdr.len) { +#ifdef IPSEC_DEBUG + printf("key_parse: invalid message length.\n"); +#endif + pfkeystat.out_invlen++; + error = EINVAL; + goto senderror; + } - /* check version */ if (msg->sadb_msg_version != PF_KEY_V2) { +#ifdef IPSEC_DEBUG printf("key_parse: PF_KEY version %u is mismatched.\n", msg->sadb_msg_version); - return EINVAL; +#endif + pfkeystat.out_invver++; + error = EINVAL; + goto senderror; } - /* check type */ if (msg->sadb_msg_type > SADB_MAX) { +#ifdef IPSEC_DEBUG printf("key_parse: invalid type %u is passed.\n", msg->sadb_msg_type); - msg->sadb_msg_errno = EINVAL; - return orglen; +#endif + pfkeystat.out_invmsgtype++; + error = EINVAL; + goto senderror; } - /* align message. */ - if (key_align(msg, mhp) != 0) { - msg->sadb_msg_errno = EINVAL; - return orglen; + /* for old-fashioned code - should be nuked */ + if (m->m_pkthdr.len > MCLBYTES) { + m_freem(m); + return ENOBUFS; } + if (m->m_next) { + struct mbuf *n; + MGETHDR(n, M_DONTWAIT, MT_DATA); + if (n && m->m_pkthdr.len > MHLEN) { + MCLGET(n, M_DONTWAIT); + if ((n->m_flags & M_EXT) == 0) { + m_free(n); + n = NULL; + } + } + if (!n) { + m_freem(m); + return ENOBUFS; + } + m_copydata(m, 0, m->m_pkthdr.len, mtod(n, caddr_t)); + n->m_pkthdr.len = n->m_len = m->m_pkthdr.len; + n->m_next = NULL; + m_freem(m); + m = n; + } + + /* align the mbuf chain so that extensions are in contiguous region. */ + error = key_align(m, &mh); + if (error) + return error; + + if (m->m_next) { /*XXX*/ + m_freem(m); + return ENOBUFS; + } + + msg = mh.msg; + /* check SA type */ switch (msg->sadb_msg_satype) { case SADB_SATYPE_UNSPEC: switch (msg->sadb_msg_type) { case SADB_GETSPI: case SADB_UPDATE: case SADB_ADD: case SADB_DELETE: case SADB_GET: case SADB_ACQUIRE: case SADB_EXPIRE: +#ifdef IPSEC_DEBUG printf("key_parse: must specify satype " "when msg type=%u.\n", msg->sadb_msg_type); - msg->sadb_msg_errno = EINVAL; - return orglen; +#endif + pfkeystat.out_invsatype++; + error = EINVAL; + goto senderror; } break; case SADB_SATYPE_AH: case SADB_SATYPE_ESP: +#if 1 /*nonstandard*/ + case SADB_X_SATYPE_IPCOMP: +#endif switch (msg->sadb_msg_type) { case SADB_X_SPDADD: case SADB_X_SPDDELETE: case SADB_X_SPDGET: case SADB_X_SPDDUMP: case SADB_X_SPDFLUSH: - printf("key_parse: illegal satype=%u\n", msg->sadb_msg_type); - msg->sadb_msg_errno = EINVAL; - return orglen; + case SADB_X_SPDSETIDX: + case SADB_X_SPDUPDATE: + case SADB_X_SPDDELETE2: +#ifdef IPSEC_DEBUG + printf("key_parse: illegal satype=%u\n", + msg->sadb_msg_type); +#endif + pfkeystat.out_invsatype++; + error = EINVAL; + goto senderror; } break; case SADB_SATYPE_RSVP: case SADB_SATYPE_OSPFV2: case SADB_SATYPE_RIPV2: case SADB_SATYPE_MIP: +#ifdef IPSEC_DEBUG printf("key_parse: type %u isn't supported.\n", msg->sadb_msg_satype); - msg->sadb_msg_errno = EOPNOTSUPP; - return orglen; - case 1: /* XXX: What does it do ? */ +#endif + pfkeystat.out_invsatype++; + error = EOPNOTSUPP; + goto senderror; + case 1: /* XXX: What does it do? */ if (msg->sadb_msg_type == SADB_X_PROMISC) break; /*FALLTHROUGH*/ default: +#ifdef IPSEC_DEBUG printf("key_parse: invalid type %u is passed.\n", msg->sadb_msg_satype); - msg->sadb_msg_errno = EINVAL; - return orglen; +#endif + pfkeystat.out_invsatype++; + error = EINVAL; + goto senderror; } /* check field of upper layer protocol and address family */ - if (mhp[SADB_EXT_ADDRESS_SRC] != NULL - && mhp[SADB_EXT_ADDRESS_DST] != NULL) { + if (mh.ext[SADB_EXT_ADDRESS_SRC] != NULL + && mh.ext[SADB_EXT_ADDRESS_DST] != NULL) { struct sadb_address *src0, *dst0; - u_int prefix; + u_int plen; - src0 = (struct sadb_address *)(mhp[SADB_EXT_ADDRESS_SRC]); - dst0 = (struct sadb_address *)(mhp[SADB_EXT_ADDRESS_DST]); + src0 = (struct sadb_address *)(mh.ext[SADB_EXT_ADDRESS_SRC]); + dst0 = (struct sadb_address *)(mh.ext[SADB_EXT_ADDRESS_DST]); /* check upper layer protocol */ if (src0->sadb_address_proto != dst0->sadb_address_proto) { +#ifdef IPSEC_DEBUG printf("key_parse: upper layer protocol mismatched.\n"); - msg->sadb_msg_errno = EINVAL; - return orglen; +#endif + pfkeystat.out_invaddr++; + error = EINVAL; + goto senderror; } /* check family */ - if (PFKEY_ADDR_SADDR(src0)->sa_family - != PFKEY_ADDR_SADDR(dst0)->sa_family) { + if (PFKEY_ADDR_SADDR(src0)->sa_family != + PFKEY_ADDR_SADDR(dst0)->sa_family) { +#ifdef IPSEC_DEBUG printf("key_parse: address family mismatched.\n"); - msg->sadb_msg_errno = EINVAL; - return orglen; +#endif + pfkeystat.out_invaddr++; + error = EINVAL; + goto senderror; } + if (PFKEY_ADDR_SADDR(src0)->sa_len != + PFKEY_ADDR_SADDR(dst0)->sa_len) { +#ifdef IPSEC_DEBUG + printf("key_parse: address struct size mismatched.\n"); +#endif + pfkeystat.out_invaddr++; + error = EINVAL; + goto senderror; + } - prefix = _INALENBYAF(PFKEY_ADDR_SADDR(src0)->sa_family) << 3; - - /* check max prefixlen */ - if (prefix < src0->sadb_address_prefixlen - || prefix < dst0->sadb_address_prefixlen) { - printf("key_parse: illegal prefixlen.\n"); - msg->sadb_msg_errno = EINVAL; - return orglen; + switch (PFKEY_ADDR_SADDR(src0)->sa_family) { + case AF_INET: + if (PFKEY_ADDR_SADDR(src0)->sa_len != + sizeof(struct sockaddr_in)) { + pfkeystat.out_invaddr++; + error = EINVAL; + goto senderror; + } + break; + case AF_INET6: + if (PFKEY_ADDR_SADDR(src0)->sa_len != + sizeof(struct sockaddr_in6)) { + pfkeystat.out_invaddr++; + error = EINVAL; + goto senderror; + } + break; + default: +#ifdef IPSEC_DEBUG + printf("key_parse: unsupported address family.\n"); +#endif + pfkeystat.out_invaddr++; + error = EAFNOSUPPORT; + goto senderror; } switch (PFKEY_ADDR_SADDR(src0)->sa_family) { case AF_INET: + plen = sizeof(struct in_addr) << 3; + break; case AF_INET6: + plen = sizeof(struct in6_addr) << 3; break; default: - printf("key_parse: invalid address family.\n"); - msg->sadb_msg_errno = EINVAL; - return orglen; + plen = 0; /*fool gcc*/ + break; } + /* check max prefix length */ + if (src0->sadb_address_prefixlen > plen || + dst0->sadb_address_prefixlen > plen) { +#ifdef IPSEC_DEBUG + printf("key_parse: illegal prefixlen.\n"); +#endif + pfkeystat.out_invaddr++; + error = EINVAL; + goto senderror; + } + /* * prefixlen == 0 is valid because there can be a case when * all addresses are matched. */ } - switch (msg->sadb_msg_type) { - case SADB_GETSPI: - if ((newmsg = key_getspi(mhp)) == NULL) - return orglen; - if (targetp) - *targetp = KEY_SENDUP_ALL; - break; + if (msg->sadb_msg_type >= sizeof(key_typesw)/sizeof(key_typesw[0]) || + key_typesw[msg->sadb_msg_type] == NULL) { + pfkeystat.out_invmsgtype++; + error = EINVAL; + goto senderror; + } - case SADB_UPDATE: - if ((newmsg = key_update(mhp)) == NULL) - return orglen; - if (targetp) - *targetp = KEY_SENDUP_ALL; - break; + return (*key_typesw[msg->sadb_msg_type])(so, m, &mh); - case SADB_ADD: - if ((newmsg = key_add(mhp)) == NULL) - return orglen; - if (targetp) - *targetp = KEY_SENDUP_ALL; - break; +senderror: + msg->sadb_msg_errno = error; + return key_sendup_mbuf(so, m, target); +} - case SADB_DELETE: - if ((newmsg = key_delete(mhp)) == NULL) - return orglen; - if (targetp) - *targetp = KEY_SENDUP_ALL; - break; +static int +key_senderror(so, m, code) + struct socket *so; + struct mbuf *m; + int code; +{ + struct sadb_msg *msg; - case SADB_GET: - if ((newmsg = key_get(mhp)) == NULL) - return orglen; - break; + if (m->m_len < sizeof(struct sadb_msg)) + panic("invalid mbuf passed to key_senderror"); - case SADB_ACQUIRE: - if ((newmsg = key_acquire2(mhp)) == NULL) - return orglen; - - if (newmsg == (struct sadb_msg *)~0) { - /* - * It's not need to reply because of the message - * that was reporting an error occured from the KMd. - */ - KFREE(msg); - return 0; - } - break; - - case SADB_REGISTER: - if ((newmsg = key_register(mhp, so)) == NULL) - return orglen; - if (targetp) - *targetp = KEY_SENDUP_REGISTERED; - break; - - case SADB_EXPIRE: - printf("key_parse: why is SADB_EXPIRE received ?\n"); - msg->sadb_msg_errno = EINVAL; - if (targetp) - *targetp = KEY_SENDUP_ALL; - return orglen; - - case SADB_FLUSH: - if ((newmsg = key_flush(mhp)) == NULL) - return orglen; - if (targetp) - *targetp = KEY_SENDUP_ALL; - break; - - case SADB_DUMP: - /* key_dump will call key_sendup() on her own */ - error = key_dump(mhp, so, KEY_SENDUP_ONE); - if (error) { - msg->sadb_msg_errno = error; - return orglen; - } else { - KFREE(msg); - return 0; - } - break; - - case SADB_X_PROMISC: - /* everything is handled in key_promisc() */ - key_promisc(mhp, so); - KFREE(msg); - return 0; /*nothing to reply*/ - - case SADB_X_PCHANGE: - printf("key_parse: SADB_X_PCHANGE isn't supported.\n"); - msg->sadb_msg_errno = EINVAL; - return orglen; - - case SADB_X_SPDADD: - if ((newmsg = key_spdadd(mhp)) == NULL) - return orglen; - if (targetp) - *targetp = KEY_SENDUP_ALL; - break; - - case SADB_X_SPDDELETE: - if ((newmsg = key_spddelete(mhp)) == NULL) - return orglen; - if (targetp) - *targetp = KEY_SENDUP_ALL; - break; - - case SADB_X_SPDDUMP: - /* key_spddump will call key_sendup() on her own */ - error = key_spddump(mhp, so, KEY_SENDUP_ONE); - if (error) { - msg->sadb_msg_errno = error; - return orglen; - } else { - KFREE(msg); - return 0; - } - break; - - - case SADB_X_SPDFLUSH: - if ((newmsg = key_spdflush(mhp)) == NULL) - return orglen; - if (targetp) - *targetp = KEY_SENDUP_ALL; - break; - - default: - msg->sadb_msg_errno = EOPNOTSUPP; - return orglen; - } - - /* switch from old sadb_msg to new one if success. */ - KFREE(msg); - *msgp = newmsg; - - return PFKEY_UNUNIT64((*msgp)->sadb_msg_len); + msg = mtod(m, struct sadb_msg *); + msg->sadb_msg_errno = code; + return key_sendup_mbuf(so, m, KEY_SENDUP_ONE); } /* * set the pointer to each header into message buffer. - * IN: msg: pointer to message buffer. - * mhp: pointer to the buffer allocated like below: - * caddr_t mhp[SADB_EXT_MAX + 1]; - * OUT: 0: - * EINVAL: + * m will be freed on error. + * XXX larger-than-MCLBYTES extension? */ static int -key_align(msg, mhp) - struct sadb_msg *msg; - caddr_t *mhp; +key_align(m, mhp) + struct mbuf *m; + struct sadb_msghdr *mhp; { + struct mbuf *n; struct sadb_ext *ext; - int tlen, extlen; - int i; + size_t off, end; + int extlen; + int toff; /* sanity check */ - if (msg == NULL || mhp == NULL) + if (m == NULL || mhp == NULL) panic("key_align: NULL pointer is passed.\n"); + if (m->m_len < sizeof(struct sadb_msg)) + panic("invalid mbuf passed to key_align"); /* initialize */ - for (i = 0; i < SADB_EXT_MAX + 1; i++) - mhp[i] = NULL; + bzero(mhp, sizeof(*mhp)); - mhp[0] = (caddr_t)msg; + mhp->msg = mtod(m, struct sadb_msg *); + mhp->ext[0] = (struct sadb_ext *)mhp->msg; /*XXX backward compat */ - tlen = PFKEY_UNUNIT64(msg->sadb_msg_len) - sizeof(struct sadb_msg); - ext = (struct sadb_ext *)((caddr_t)msg + sizeof(struct sadb_msg)); - - while (tlen > 0) { - /* duplicate check */ - /* XXX Are there duplication either KEY_AUTH or KEY_ENCRYPT ?*/ - if (mhp[ext->sadb_ext_type] != NULL) { - printf("key_align: duplicate ext_type %u is passed.\n", - ext->sadb_ext_type); - return EINVAL; + end = PFKEY_UNUNIT64(mhp->msg->sadb_msg_len); + extlen = end; /*just in case extlen is not updated*/ + for (off = sizeof(struct sadb_msg); off < end; off += extlen) { + n = m_pulldown(m, off, sizeof(struct sadb_ext), &toff); + if (!n) { + /* m is already freed */ + return ENOBUFS; } + ext = (struct sadb_ext *)(mtod(n, caddr_t) + toff); /* set pointer */ switch (ext->sadb_ext_type) { case SADB_EXT_SA: - case SADB_EXT_LIFETIME_CURRENT: - case SADB_EXT_LIFETIME_HARD: - case SADB_EXT_LIFETIME_SOFT: case SADB_EXT_ADDRESS_SRC: case SADB_EXT_ADDRESS_DST: case SADB_EXT_ADDRESS_PROXY: + case SADB_EXT_LIFETIME_CURRENT: + case SADB_EXT_LIFETIME_HARD: + case SADB_EXT_LIFETIME_SOFT: case SADB_EXT_KEY_AUTH: - /* must to be chek weak keys. */ case SADB_EXT_KEY_ENCRYPT: - /* must to be chek weak keys. */ case SADB_EXT_IDENTITY_SRC: case SADB_EXT_IDENTITY_DST: case SADB_EXT_SENSITIVITY: case SADB_EXT_PROPOSAL: case SADB_EXT_SUPPORTED_AUTH: case SADB_EXT_SUPPORTED_ENCRYPT: case SADB_EXT_SPIRANGE: case SADB_X_EXT_POLICY: - mhp[ext->sadb_ext_type] = (caddr_t)ext; + case SADB_X_EXT_SA2: + /* duplicate check */ + /* + * XXX Are there duplication payloads of either + * KEY_AUTH or KEY_ENCRYPT ? + */ + if (mhp->ext[ext->sadb_ext_type] != NULL) { +#ifdef IPSEC_DEBUG + printf("key_align: duplicate ext_type %u " + "is passed.\n", + ext->sadb_ext_type); +#endif + m_freem(m); + pfkeystat.out_dupext++; + return EINVAL; + } break; default: +#ifdef IPSEC_DEBUG printf("key_align: invalid ext_type %u is passed.\n", ext->sadb_ext_type); +#endif + m_freem(m); + pfkeystat.out_invexttype++; return EINVAL; } extlen = PFKEY_UNUNIT64(ext->sadb_ext_len); - tlen -= extlen; - ext = (struct sadb_ext *)((caddr_t)ext + extlen); + + if (key_validate_ext(ext, extlen)) { + m_freem(m); + pfkeystat.out_invlen++; + return EINVAL; + } + + n = m_pulldown(m, off, extlen, &toff); + if (!n) { + /* m is already freed */ + return ENOBUFS; + } + ext = (struct sadb_ext *)(mtod(n, caddr_t) + toff); + + mhp->ext[ext->sadb_ext_type] = ext; + mhp->extoff[ext->sadb_ext_type] = off; + mhp->extlen[ext->sadb_ext_type] = extlen; } + if (off != end) { + m_freem(m); + pfkeystat.out_invlen++; + return EINVAL; + } + return 0; } +static int +key_validate_ext(ext, len) + const struct sadb_ext *ext; + int len; +{ + struct sockaddr *sa; + enum { NONE, ADDR } checktype = NONE; + int baselen; + const int sal = offsetof(struct sockaddr, sa_len) + sizeof(sa->sa_len); + + if (len != PFKEY_UNUNIT64(ext->sadb_ext_len)) + return EINVAL; + + /* if it does not match minimum/maximum length, bail */ + if (ext->sadb_ext_type >= sizeof(minsize) / sizeof(minsize[0]) || + ext->sadb_ext_type >= sizeof(maxsize) / sizeof(maxsize[0])) + return EINVAL; + if (!minsize[ext->sadb_ext_type] || len < minsize[ext->sadb_ext_type]) + return EINVAL; + if (maxsize[ext->sadb_ext_type] && len > maxsize[ext->sadb_ext_type]) + return EINVAL; + + /* more checks based on sadb_ext_type XXX need more */ + switch (ext->sadb_ext_type) { + case SADB_EXT_ADDRESS_SRC: + case SADB_EXT_ADDRESS_DST: + case SADB_EXT_ADDRESS_PROXY: + baselen = PFKEY_ALIGN8(sizeof(struct sadb_address)); + checktype = ADDR; + break; + case SADB_EXT_IDENTITY_SRC: + case SADB_EXT_IDENTITY_DST: + if (((struct sadb_ident *)ext)->sadb_ident_type == + SADB_X_IDENTTYPE_ADDR) { + baselen = PFKEY_ALIGN8(sizeof(struct sadb_ident)); + checktype = ADDR; + } else + checktype = NONE; + break; + default: + checktype = NONE; + break; + } + + switch (checktype) { + case NONE: + break; + case ADDR: + sa = (struct sockaddr *)((caddr_t)ext + baselen); + if (len < baselen + sal) + return EINVAL; + if (baselen + PFKEY_ALIGN8(sa->sa_len) != len) + return EINVAL; + break; + } + + return 0; +} + void key_init() { int i; bzero((caddr_t)&key_cb, sizeof(key_cb)); for (i = 0; i < IPSEC_DIR_MAX; i++) { LIST_INIT(&sptree[i]); } LIST_INIT(&sahtree); for (i = 0; i <= SADB_SATYPE_MAX; i++) { LIST_INIT(®tree[i]); } #ifndef IPSEC_NONBLOCK_ACQUIRE LIST_INIT(&acqtree); #endif + LIST_INIT(&spacqtree); /* system default */ ip4_def_policy.policy = IPSEC_POLICY_NONE; ip4_def_policy.refcnt++; /*never reclaim this*/ #ifdef INET6 ip6_def_policy.policy = IPSEC_POLICY_NONE; ip6_def_policy.refcnt++; /*never reclaim this*/ #endif #ifndef IPSEC_DEBUG2 - timeout((void *)key_timehandler, (void *)0, 100); + timeout((void *)key_timehandler, (void *)0, hz); #endif /*IPSEC_DEBUG2*/ /* initialize key statistics */ keystat.getspi_count = 1; printf("IPsec: Initialized Security Association Processing.\n"); return; } /* * XXX: maybe This function is called after INBOUND IPsec processing. * * Special check for tunnel-mode packets. * We must make some checks for consistency between inner and outer IP header. * * xxx more checks to be provided */ int key_checktunnelsanity(sav, family, src, dst) struct secasvar *sav; u_int family; caddr_t src; caddr_t dst; { /* sanity check */ if (sav->sah == NULL) panic("sav->sah == NULL at key_checktunnelsanity"); /* XXX: check inner IP header */ return 1; } #if 0 -#ifdef __FreeBSD__ -#define hostnamelen strlen(hostname) -#endif +#define hostnamelen strlen(hostname) /* * Get FQDN for the host. * If the administrator configured hostname (by hostname(1)) without * domain name, returns nothing. */ static const char * key_getfqdn() { int i; int hasdot; static char fqdn[MAXHOSTNAMELEN + 1]; if (!hostnamelen) return NULL; /* check if it comes with domain name. */ hasdot = 0; for (i = 0; i < hostnamelen; i++) { if (hostname[i] == '.') hasdot++; } if (!hasdot) return NULL; /* NOTE: hostname may not be NUL-terminated. */ bzero(fqdn, sizeof(fqdn)); bcopy(hostname, fqdn, hostnamelen); fqdn[hostnamelen] = '\0'; return fqdn; } /* * get username@FQDN for the host/user. */ static const char * key_getuserfqdn() { const char *host; static char userfqdn[MAXHOSTNAMELEN + MAXLOGNAME + 2]; struct proc *p = curproc; char *q; if (!p || !p->p_pgrp || !p->p_pgrp->pg_session) return NULL; if (!(host = key_getfqdn())) return NULL; /* NOTE: s_login may not be-NUL terminated. */ bzero(userfqdn, sizeof(userfqdn)); bcopy(p->p_pgrp->pg_session->s_login, userfqdn, MAXLOGNAME); userfqdn[MAXLOGNAME] = '\0'; /* safeguard */ q = userfqdn + strlen(userfqdn); *q++ = '@'; bcopy(host, q, strlen(host)); q += strlen(host); *q++ = '\0'; return userfqdn; } #endif /* record data transfer on SA, and update timestamps */ void key_sa_recordxfer(sav, m) struct secasvar *sav; struct mbuf *m; { if (!sav) panic("key_sa_recordxfer called with sav == NULL"); if (!m) panic("key_sa_recordxfer called with m == NULL"); if (!sav->lft_c) return; + /* + * XXX Currently, there is a difference of bytes size + * between inbound and outbound processing. + */ sav->lft_c->sadb_lifetime_bytes += m->m_pkthdr.len; /* to check bytes lifetime is done in key_timehandler(). */ /* * We use the number of packets as the unit of * sadb_lifetime_allocations. We increment the variable * whenever {esp,ah}_{in,out}put is called. */ sav->lft_c->sadb_lifetime_allocations++; /* XXX check for expires? */ /* * NOTE: We record CURRENT sadb_lifetime_usetime by using wall clock, * in seconds. HARD and SOFT lifetime are measured by the time * difference (again in seconds) from sadb_lifetime_usetime. * * usetime * v expire expire * -----+-----+--------+---> t * <--------------> HARD * <-----> SOFT */ { struct timeval tv; microtime(&tv); sav->lft_c->sadb_lifetime_usetime = tv.tv_sec; /* XXX check for expires? */ } return; } /* dumb version */ void key_sa_routechange(dst) struct sockaddr *dst; { struct secashead *sah; struct route *ro; - __LIST_FOREACH(sah, &sahtree, chain) { + LIST_FOREACH(sah, &sahtree, chain) { ro = &sah->sa_route; if (ro->ro_rt && dst->sa_len == ro->ro_dst.sa_len && bcmp(dst, &ro->ro_dst, dst->sa_len) == 0) { RTFREE(ro->ro_rt); ro->ro_rt = (struct rtentry *)NULL; } } return; } static void key_sa_chgstate(sav, state) struct secasvar *sav; u_int8_t state; { if (sav == NULL) panic("key_sa_chgstate called with sav == NULL"); if (sav->state == state) return; if (__LIST_CHAINED(sav)) LIST_REMOVE(sav, chain); sav->state = state; LIST_INSERT_HEAD(&sav->sah->savtree[state], sav, chain); +} + +/* XXX too much? */ +static struct mbuf * +key_alloc_mbuf(l) + int l; +{ + struct mbuf *m = NULL, *n; + int len, t; + + len = l; + while (len > 0) { + MGET(n, M_DONTWAIT, MT_DATA); + if (n && len > MLEN) + MCLGET(n, M_DONTWAIT); + if (!n) { + m_freem(m); + return NULL; + } + + n->m_next = NULL; + n->m_len = 0; + n->m_len = M_TRAILINGSPACE(n); + /* use the bottom of mbuf, hoping we can prepend afterwards */ + if (n->m_len > len) { + t = (n->m_len - len) & ~(sizeof(long) - 1); + n->m_data += t; + n->m_len = len; + } + + len -= n->m_len; + + if (m) + m_cat(m, n); + else + m = n; + } + + return m; } Index: head/sys/netkey/key.h =================================================================== --- head/sys/netkey/key.h (revision 62586) +++ head/sys/netkey/key.h (revision 62587) @@ -1,78 +1,77 @@ +/* $FreeBSD$ */ +/* $KAME: key.h,v 1.17 2000/06/12 07:01:13 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ -/* $Id: key.h,v 1.1.6.1.6.1 1999/05/17 17:03:14 itojun Exp $ */ - #ifndef _NETKEY_KEY_H_ -#define _NETKEY_KEY_H_ +#define _NETKEY_KEY_H_ #ifdef _KERNEL extern struct key_cb key_cb; struct secpolicy; struct secpolicyindex; struct ipsecrequest; struct secasvar; struct sockaddr; struct socket; struct sadb_msg; struct sadb_x_policy; -extern struct secpolicy *key_allocsp __P((struct secpolicyindex *spidx, - u_int dir)); -extern int key_checkrequest __P((struct ipsecrequest *isr)); -extern struct secasvar *key_allocsa __P((u_int family, caddr_t src, caddr_t dst, - u_int proto, u_int32_t spi)); -extern void key_freesp __P((struct secpolicy *sp)); -extern void key_freeso __P((struct socket *so)); -extern void key_freesav __P((struct secasvar *sav)); +extern struct secpolicy *key_allocsp __P((struct secpolicyindex *, u_int)); +extern int key_checkrequest + __P((struct ipsecrequest *isr, struct secasindex *)); +extern struct secasvar *key_allocsa __P((u_int, caddr_t, caddr_t, + u_int, u_int32_t)); +extern void key_freesp __P((struct secpolicy *)); +extern void key_freeso __P((struct socket *)); +extern void key_freesav __P((struct secasvar *)); extern struct secpolicy *key_newsp __P((void)); -extern struct secpolicy *key_msg2sp __P((struct sadb_x_policy *xpl0)); -extern struct sadb_x_policy *key_sp2msg __P((struct secpolicy *sp)); -extern int key_ismyaddr __P((u_int family, caddr_t addr)); +extern struct secpolicy *key_msg2sp __P((struct sadb_x_policy *, + size_t, int *)); +extern struct mbuf *key_sp2msg __P((struct secpolicy *)); +extern int key_ismyaddr __P((struct sockaddr *)); +extern int key_spdacquire __P((struct secpolicy *)); extern void key_timehandler __P((void)); -extern void key_srandom __P((void)); -extern void key_freereg __P((struct socket *so)); -extern int key_parse __P((struct sadb_msg **msgp, struct socket *so, - int *targetp)); +extern void key_freereg __P((struct socket *)); +extern int key_parse __P((struct mbuf *, struct socket *)); extern void key_init __P((void)); -extern int key_checktunnelsanity __P((struct secasvar *sav, u_int family, - caddr_t src, caddr_t dst)); -extern void key_sa_recordxfer __P((struct secasvar *sav, struct mbuf *m)); -extern void key_sa_routechange __P((struct sockaddr *dst)); +extern int key_checktunnelsanity __P((struct secasvar *, u_int, + caddr_t, caddr_t)); +extern void key_sa_recordxfer __P((struct secasvar *, struct mbuf *)); +extern void key_sa_routechange __P((struct sockaddr *)); #ifdef MALLOC_DECLARE MALLOC_DECLARE(M_SECA); #endif /* MALLOC_DECLARE */ -#endif /* _KERNEL */ +#endif /* defined(_KERNEL) */ #endif /* _NETKEY_KEY_H_ */ Index: head/sys/netkey/key_debug.c =================================================================== --- head/sys/netkey/key_debug.c (revision 62586) +++ head/sys/netkey/key_debug.c (revision 62587) @@ -1,683 +1,753 @@ +/* $FreeBSD$ */ +/* $KAME: key_debug.c,v 1.23 2000/07/04 04:08:15 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ -/* KAME @(#)$Id: key_debug.c,v 1.1.6.2.4.3 1999/07/06 12:05:13 itojun Exp $ */ - #ifdef _KERNEL +#include "opt_inet.h" #include "opt_inet6.h" -#include "opt_ipsec.h" #endif #include #include #ifdef _KERNEL #include #include #endif #include #include #include +#ifdef IPSEC_DEBUG #include +#else +#define KEYDEBUG(lev,arg) +#endif #include -#include #include #ifndef _KERNEL #include #include #include -#endif +#endif /* !_KERNEL */ #if !defined(_KERNEL) || (defined(_KERNEL) && defined(IPSEC_DEBUG)) static void kdebug_sadb_prop __P((struct sadb_ext *)); static void kdebug_sadb_identity __P((struct sadb_ext *)); static void kdebug_sadb_supported __P((struct sadb_ext *)); static void kdebug_sadb_lifetime __P((struct sadb_ext *)); static void kdebug_sadb_sa __P((struct sadb_ext *)); static void kdebug_sadb_address __P((struct sadb_ext *)); static void kdebug_sadb_key __P((struct sadb_ext *)); +static void kdebug_sadb_x_sa2 __P((struct sadb_ext *)); #ifdef _KERNEL static void kdebug_secreplay __P((struct secreplay *)); #endif #ifndef _KERNEL -#define panic(param) { printf(param); exit(-1); } +#define panic(param) { printf(param); exit(-1); } #endif /* NOTE: host byte order */ /* %%%: about struct sadb_msg */ void kdebug_sadb(base) struct sadb_msg *base; { struct sadb_ext *ext; int tlen, extlen; /* sanity check */ if (base == NULL) panic("kdebug_sadb: NULL pointer was passed.\n"); printf("sadb_msg{ version=%u type=%u errno=%u satype=%u\n", base->sadb_msg_version, base->sadb_msg_type, base->sadb_msg_errno, base->sadb_msg_satype); - printf(" len=%u mode=%u reserved=%u seq=%u pid=%u }\n", - base->sadb_msg_len, base->sadb_msg_mode, - base->sadb_msg_reserved, base->sadb_msg_seq, base->sadb_msg_pid); + printf(" len=%u reserved=%u seq=%u pid=%u\n", + base->sadb_msg_len, base->sadb_msg_reserved, + base->sadb_msg_seq, base->sadb_msg_pid); tlen = PFKEY_UNUNIT64(base->sadb_msg_len) - sizeof(struct sadb_msg); ext = (struct sadb_ext *)((caddr_t)base + sizeof(struct sadb_msg)); while (tlen > 0) { printf("sadb_ext{ len=%u type=%u }\n", ext->sadb_ext_len, ext->sadb_ext_type); if (ext->sadb_ext_len == 0) { printf("kdebug_sadb: invalid ext_len=0 was passed.\n"); return; } + if (ext->sadb_ext_len > tlen) { + printf("kdebug_sadb: ext_len exceeds end of buffer.\n"); + return; + } switch (ext->sadb_ext_type) { case SADB_EXT_SA: kdebug_sadb_sa(ext); break; case SADB_EXT_LIFETIME_CURRENT: case SADB_EXT_LIFETIME_HARD: case SADB_EXT_LIFETIME_SOFT: kdebug_sadb_lifetime(ext); break; case SADB_EXT_ADDRESS_SRC: case SADB_EXT_ADDRESS_DST: case SADB_EXT_ADDRESS_PROXY: kdebug_sadb_address(ext); break; case SADB_EXT_KEY_AUTH: case SADB_EXT_KEY_ENCRYPT: kdebug_sadb_key(ext); break; case SADB_EXT_IDENTITY_SRC: case SADB_EXT_IDENTITY_DST: kdebug_sadb_identity(ext); break; case SADB_EXT_SENSITIVITY: break; case SADB_EXT_PROPOSAL: kdebug_sadb_prop(ext); break; case SADB_EXT_SUPPORTED_AUTH: case SADB_EXT_SUPPORTED_ENCRYPT: kdebug_sadb_supported(ext); break; case SADB_EXT_SPIRANGE: case SADB_X_EXT_KMPRIVATE: break; case SADB_X_EXT_POLICY: kdebug_sadb_x_policy(ext); break; + case SADB_X_EXT_SA2: + kdebug_sadb_x_sa2(ext); + break; default: printf("kdebug_sadb: invalid ext_type %u was passed.\n", ext->sadb_ext_type); return; } extlen = PFKEY_UNUNIT64(ext->sadb_ext_len); tlen -= extlen; ext = (struct sadb_ext *)((caddr_t)ext + extlen); } return; } static void kdebug_sadb_prop(ext) struct sadb_ext *ext; { struct sadb_prop *prop = (struct sadb_prop *)ext; struct sadb_comb *comb; int len; /* sanity check */ if (ext == NULL) panic("kdebug_sadb_prop: NULL pointer was passed.\n"); len = (PFKEY_UNUNIT64(prop->sadb_prop_len) - sizeof(*prop)) / sizeof(*comb); comb = (struct sadb_comb *)(prop + 1); printf("sadb_prop{ replay=%u\n", prop->sadb_prop_replay); while (len--) { printf("sadb_comb{ auth=%u encrypt=%u " "flags=0x%04x reserved=0x%08x\n", comb->sadb_comb_auth, comb->sadb_comb_encrypt, comb->sadb_comb_flags, comb->sadb_comb_reserved); printf(" auth_minbits=%u auth_maxbits=%u " "encrypt_minbits=%u encrypt_maxbits=%u\n", comb->sadb_comb_auth_minbits, comb->sadb_comb_auth_maxbits, comb->sadb_comb_encrypt_minbits, comb->sadb_comb_encrypt_maxbits); printf(" soft_alloc=%u hard_alloc=%u " "soft_bytes=%lu hard_bytes=%lu\n", comb->sadb_comb_soft_allocations, comb->sadb_comb_hard_allocations, (unsigned long)comb->sadb_comb_soft_bytes, (unsigned long)comb->sadb_comb_hard_bytes); printf(" soft_alloc=%lu hard_alloc=%lu " "soft_bytes=%lu hard_bytes=%lu }\n", (unsigned long)comb->sadb_comb_soft_addtime, (unsigned long)comb->sadb_comb_hard_addtime, (unsigned long)comb->sadb_comb_soft_usetime, (unsigned long)comb->sadb_comb_hard_usetime); comb++; } printf("}\n"); return; } static void kdebug_sadb_identity(ext) struct sadb_ext *ext; { struct sadb_ident *id = (struct sadb_ident *)ext; int len; /* sanity check */ if (ext == NULL) panic("kdebug_sadb_identity: NULL pointer was passed.\n"); len = PFKEY_UNUNIT64(id->sadb_ident_len) - sizeof(*id); printf("sadb_ident_%s{", id->sadb_ident_exttype == SADB_EXT_IDENTITY_SRC ? "src" : "dst"); - printf(" type=%d id=%lu", - id->sadb_ident_type, (u_long)id->sadb_ident_id); - if (len) { + switch (id->sadb_ident_type) { + default: + printf(" type=%d id=%lu", + id->sadb_ident_type, (u_long)id->sadb_ident_id); + if (len) { #ifdef _KERNEL - ipsec_hexdump((caddr_t)(id + 1), len); /*XXX cast ?*/ + ipsec_hexdump((caddr_t)(id + 1), len); /*XXX cast ?*/ #else - char *p, *ep; - printf("\n str=\""); - p = (char *)(id + 1); - ep = p + len; - for (/*nothing*/; *p && p < ep; p++) { - if (isprint(*p)) - printf("%c", *p & 0xff); - else - printf("\\%03o", *p & 0xff); - } + char *p, *ep; + printf("\n str=\""); + p = (char *)(id + 1); + ep = p + len; + for (/*nothing*/; *p && p < ep; p++) { + if (isprint(*p)) + printf("%c", *p & 0xff); + else + printf("\\%03o", *p & 0xff); + } #endif - printf("\""); + printf("\""); + } + break; } + printf(" }\n"); return; } static void kdebug_sadb_supported(ext) struct sadb_ext *ext; { struct sadb_supported *sup = (struct sadb_supported *)ext; struct sadb_alg *alg; int len; /* sanity check */ if (ext == NULL) panic("kdebug_sadb_supported: NULL pointer was passed.\n"); len = (PFKEY_UNUNIT64(sup->sadb_supported_len) - sizeof(*sup)) / sizeof(*alg); alg = (struct sadb_alg *)(sup + 1); printf("sadb_sup{\n"); while (len--) { printf(" { id=%d ivlen=%d min=%d max=%d }\n", alg->sadb_alg_id, alg->sadb_alg_ivlen, alg->sadb_alg_minbits, alg->sadb_alg_maxbits); alg++; } printf("}\n"); return; } static void kdebug_sadb_lifetime(ext) struct sadb_ext *ext; { struct sadb_lifetime *lft = (struct sadb_lifetime *)ext; /* sanity check */ if (ext == NULL) printf("kdebug_sadb_lifetime: NULL pointer was passed.\n"); printf("sadb_lifetime{ alloc=%u, bytes=%u\n", lft->sadb_lifetime_allocations, (u_int32_t)lft->sadb_lifetime_bytes); printf(" addtime=%u, usetime=%u }\n", (u_int32_t)lft->sadb_lifetime_addtime, (u_int32_t)lft->sadb_lifetime_usetime); return; } static void kdebug_sadb_sa(ext) struct sadb_ext *ext; { struct sadb_sa *sa = (struct sadb_sa *)ext; /* sanity check */ if (ext == NULL) panic("kdebug_sadb_sa: NULL pointer was passed.\n"); printf("sadb_sa{ spi=%u replay=%u state=%u\n", (u_int32_t)ntohl(sa->sadb_sa_spi), sa->sadb_sa_replay, sa->sadb_sa_state); printf(" auth=%u encrypt=%u flags=0x%08x }\n", sa->sadb_sa_auth, sa->sadb_sa_encrypt, sa->sadb_sa_flags); return; } static void kdebug_sadb_address(ext) struct sadb_ext *ext; { struct sadb_address *addr = (struct sadb_address *)ext; /* sanity check */ if (ext == NULL) panic("kdebug_sadb_address: NULL pointer was passed.\n"); printf("sadb_address{ proto=%u prefixlen=%u reserved=0x%02x%02x }\n", addr->sadb_address_proto, addr->sadb_address_prefixlen, ((u_char *)&addr->sadb_address_reserved)[0], ((u_char *)&addr->sadb_address_reserved)[1]); kdebug_sockaddr((struct sockaddr *)((caddr_t)ext + sizeof(*addr))); return; } static void kdebug_sadb_key(ext) struct sadb_ext *ext; { struct sadb_key *key = (struct sadb_key *)ext; /* sanity check */ if (ext == NULL) panic("kdebug_sadb_key: NULL pointer was passed.\n"); printf("sadb_key{ bits=%u reserved=%u\n", key->sadb_key_bits, key->sadb_key_reserved); printf(" key="); /* sanity check 2 */ if ((key->sadb_key_bits >> 3) > (PFKEY_UNUNIT64(key->sadb_key_len) - sizeof(struct sadb_key))) { printf("kdebug_sadb_key: key length mismatch, bit:%d len:%ld.\n", key->sadb_key_bits >> 3, (long)PFKEY_UNUNIT64(key->sadb_key_len) - sizeof(struct sadb_key)); } ipsec_hexdump((caddr_t)key + sizeof(struct sadb_key), key->sadb_key_bits >> 3); printf(" }\n"); return; } +static void +kdebug_sadb_x_sa2(ext) + struct sadb_ext *ext; +{ + struct sadb_x_sa2 *sa2 = (struct sadb_x_sa2 *)ext; + + /* sanity check */ + if (ext == NULL) + panic("kdebug_sadb_x_sa2: NULL pointer was passed.\n"); + + printf("sadb_x_sa2{ mode=%u reqid=%u\n", + sa2->sadb_x_sa2_mode, sa2->sadb_x_sa2_reqid); + printf(" reserved1=%u reserved2=%u reserved3=%u }\n", + sa2->sadb_x_sa2_reserved1, sa2->sadb_x_sa2_reserved1, + sa2->sadb_x_sa2_reserved1); + + return; +} + void kdebug_sadb_x_policy(ext) struct sadb_ext *ext; { struct sadb_x_policy *xpl = (struct sadb_x_policy *)ext; struct sockaddr *addr; /* sanity check */ if (ext == NULL) panic("kdebug_sadb_x_policy: NULL pointer was passed.\n"); - printf("sadb_x_policy{ type=%u dir=%u reserved=%x }\n", + printf("sadb_x_policy{ type=%u dir=%u id=%x }\n", xpl->sadb_x_policy_type, xpl->sadb_x_policy_dir, - xpl->sadb_x_policy_reserved); + xpl->sadb_x_policy_id); if (xpl->sadb_x_policy_type == IPSEC_POLICY_IPSEC) { int tlen; struct sadb_x_ipsecrequest *xisr; tlen = PFKEY_UNUNIT64(xpl->sadb_x_policy_len) - sizeof(*xpl); xisr = (struct sadb_x_ipsecrequest *)(xpl + 1); while (tlen > 0) { - printf(" { len=%u proto=%u mode=%u level=%u\n", + printf(" { len=%u proto=%u mode=%u level=%u reqid=%u\n", xisr->sadb_x_ipsecrequest_len, xisr->sadb_x_ipsecrequest_proto, xisr->sadb_x_ipsecrequest_mode, - xisr->sadb_x_ipsecrequest_level); + xisr->sadb_x_ipsecrequest_level, + xisr->sadb_x_ipsecrequest_reqid); - addr = (struct sockaddr *)(xisr + 1); - kdebug_sockaddr(addr); - addr = (struct sockaddr *)((caddr_t)addr + addr->sa_len); - kdebug_sockaddr(addr); + if (xisr->sadb_x_ipsecrequest_len > sizeof(*xisr)) { + addr = (struct sockaddr *)(xisr + 1); + kdebug_sockaddr(addr); + addr = (struct sockaddr *)((caddr_t)addr + + addr->sa_len); + kdebug_sockaddr(addr); + } printf(" }\n"); /* prevent infinite loop */ - if (xisr->sadb_x_ipsecrequest_len <= 0) - panic("kdebug_sadb_x_policy: wrong policy struct.\n"); + if (xisr->sadb_x_ipsecrequest_len <= 0) { + printf("kdebug_sadb_x_policy: wrong policy struct.\n"); + return; + } + /* prevent overflow */ + if (xisr->sadb_x_ipsecrequest_len > tlen) { + printf("invalid ipsec policy length\n"); + return; + } tlen -= xisr->sadb_x_ipsecrequest_len; xisr = (struct sadb_x_ipsecrequest *)((caddr_t)xisr + xisr->sadb_x_ipsecrequest_len); } if (tlen != 0) panic("kdebug_sadb_x_policy: wrong policy struct.\n"); } return; } #ifdef _KERNEL /* %%%: about SPD and SAD */ void kdebug_secpolicy(sp) struct secpolicy *sp; { /* sanity check */ if (sp == NULL) panic("kdebug_secpolicy: NULL pointer was passed.\n"); printf("secpolicy{ refcnt=%u state=%u policy=%u\n", sp->refcnt, sp->state, sp->policy); kdebug_secpolicyindex(&sp->spidx); switch (sp->policy) { case IPSEC_POLICY_DISCARD: printf(" type=discard }\n"); break; case IPSEC_POLICY_NONE: printf(" type=none }\n"); break; case IPSEC_POLICY_IPSEC: { struct ipsecrequest *isr; for (isr = sp->req; isr != NULL; isr = isr->next) { printf(" level=%u\n", isr->level); kdebug_secasindex(&isr->saidx); if (isr->sav != NULL) kdebug_secasv(isr->sav); } printf(" }\n"); } break; case IPSEC_POLICY_BYPASS: printf(" type=bypass }\n"); break; case IPSEC_POLICY_ENTRUST: printf(" type=entrust }\n"); break; default: printf("kdebug_secpolicy: Invalid policy found. %d\n", sp->policy); break; } return; } void kdebug_secpolicyindex(spidx) struct secpolicyindex *spidx; { /* sanity check */ if (spidx == NULL) panic("kdebug_secpolicyindex: NULL pointer was passed.\n"); printf("secpolicyindex{ dir=%u prefs=%u prefd=%u ul_proto=%u\n", spidx->dir, spidx->prefs, spidx->prefd, spidx->ul_proto); - ipsec_hexdump((caddr_t)&spidx->src, spidx->src.ss_len); + ipsec_hexdump((caddr_t)&spidx->src, + ((struct sockaddr *)&spidx->src)->sa_len); printf("\n"); - ipsec_hexdump((caddr_t)&spidx->dst, spidx->dst.ss_len); + ipsec_hexdump((caddr_t)&spidx->dst, + ((struct sockaddr *)&spidx->dst)->sa_len); printf("}\n"); return; } void kdebug_secasindex(saidx) struct secasindex *saidx; { /* sanity check */ if (saidx == NULL) panic("kdebug_secpolicyindex: NULL pointer was passed.\n"); printf("secasindex{ mode=%u proto=%u\n", saidx->mode, saidx->proto); - ipsec_hexdump((caddr_t)&saidx->src, saidx->src.ss_len); + ipsec_hexdump((caddr_t)&saidx->src, + ((struct sockaddr *)&saidx->src)->sa_len); printf("\n"); - ipsec_hexdump((caddr_t)&saidx->dst, saidx->dst.ss_len); + ipsec_hexdump((caddr_t)&saidx->dst, + ((struct sockaddr *)&saidx->dst)->sa_len); printf("\n"); return; } void kdebug_secasv(sav) struct secasvar *sav; { /* sanity check */ if (sav == NULL) panic("kdebug_secasv: NULL pointer was passed.\n"); printf("secas{"); kdebug_secasindex(&sav->sah->saidx); printf(" refcnt=%u state=%u auth=%u enc=%u\n", sav->refcnt, sav->state, sav->alg_auth, sav->alg_enc); printf(" spi=%u flags=%u\n", (u_int32_t)ntohl(sav->spi), sav->flags); if (sav->key_auth != NULL) kdebug_sadb_key((struct sadb_ext *)sav->key_auth); if (sav->key_enc != NULL) kdebug_sadb_key((struct sadb_ext *)sav->key_enc); if (sav->iv != NULL) { printf(" iv="); ipsec_hexdump(sav->iv, sav->ivlen ? sav->ivlen : 8); printf("\n"); } if (sav->replay != NULL) kdebug_secreplay(sav->replay); if (sav->lft_c != NULL) kdebug_sadb_lifetime((struct sadb_ext *)sav->lft_c); if (sav->lft_h != NULL) kdebug_sadb_lifetime((struct sadb_ext *)sav->lft_h); if (sav->lft_s != NULL) kdebug_sadb_lifetime((struct sadb_ext *)sav->lft_s); +#if notyet + /* XXX: misc[123] ? */ +#endif + return; } static void kdebug_secreplay(rpl) struct secreplay *rpl; { int len, l; /* sanity check */ if (rpl == NULL) panic("kdebug_secreplay: NULL pointer was passed.\n"); printf(" secreplay{ count=%u wsize=%u seq=%u lastseq=%u", rpl->count, rpl->wsize, rpl->seq, rpl->lastseq); if (rpl->bitmap == NULL) { printf(" }\n"); return; } printf("\n bitmap { "); for (len = 0; len < rpl->wsize; len++) { for (l = 7; l >= 0; l--) printf("%u", (((rpl->bitmap)[len] >> l) & 1) ? 1 : 0); } printf(" }\n"); return; } void kdebug_mbufhdr(m) struct mbuf *m; { /* sanity check */ if (m == NULL) - panic("debug_mbufhdr: NULL pointer was passed.\n"); + return; printf("mbuf(%p){ m_next:%p m_nextpkt:%p m_data:%p " "m_len:%d m_type:0x%02x m_flags:0x%02x }\n", m, m->m_next, m->m_nextpkt, m->m_data, m->m_len, m->m_type, m->m_flags); if (m->m_flags & M_PKTHDR) { printf(" m_pkthdr{ len:%d rcvif:%p }\n", m->m_pkthdr.len, m->m_pkthdr.rcvif); } + if (m->m_flags & M_EXT) { printf(" m_ext{ ext_buf:%p ext_free:%p " "ext_size:%u ext_ref:%p }\n", m->m_ext.ext_buf, m->m_ext.ext_free, m->m_ext.ext_size, m->m_ext.ext_ref); } + return; } void kdebug_mbuf(m0) struct mbuf *m0; { struct mbuf *m = m0; int i, j; - kdebug_mbufhdr(m); - printf(" m_data=\n"); for (j = 0; m; m = m->m_next) { + kdebug_mbufhdr(m); + printf(" m_data:\n"); for (i = 0; i < m->m_len; i++) { - if (i != 0 && i % 32 == 0) printf("\n"); - if (i % 4 == 0) printf(" "); + if (i && i % 32 == 0) + printf("\n"); + if (i % 4 == 0) + printf(" "); printf("%02x", mtod(m, u_char *)[i]); j++; } + printf("\n"); } - printf("\n"); - return; } #endif /* _KERNEL */ void kdebug_sockaddr(addr) struct sockaddr *addr; { + struct sockaddr_in *sin; +#ifdef INET6 + struct sockaddr_in6 *sin6; +#endif + /* sanity check */ if (addr == NULL) panic("kdebug_sockaddr: NULL pointer was passed.\n"); /* NOTE: We deal with port number as host byte order. */ - printf("sockaddr{ len=%u family=%u port=%u\n", - addr->sa_len, addr->sa_family, ntohs(_INPORTBYSA(addr))); + printf("sockaddr{ len=%u family=%u", addr->sa_len, addr->sa_family); + switch (addr->sa_family) { + case AF_INET: + sin = (struct sockaddr_in *)addr; + printf(" port=%u\n", ntohs(sin->sin_port)); + ipsec_hexdump((caddr_t)&sin->sin_addr, sizeof(sin->sin_addr)); + break; #ifdef INET6 - if (addr->sa_family == PF_INET6) { - struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)addr; + case AF_INET6: + sin6 = (struct sockaddr_in6 *)addr; + printf(" port=%u\n", ntohs(sin6->sin6_port)); printf(" flowinfo=0x%08x, scope_id=0x%08x\n", - in6->sin6_flowinfo, in6->sin6_scope_id); - } + sin6->sin6_flowinfo, sin6->sin6_scope_id); + ipsec_hexdump((caddr_t)&sin6->sin6_addr, + sizeof(sin6->sin6_addr)); + break; #endif + } - ipsec_hexdump(_INADDRBYSA(addr), _INALENBYAF(addr->sa_family)); - printf(" }\n"); return; } -#endif /* !defined(_KERNEL) || (defined(_KERNEL) && defined(IPSEC_DEBUG)) */ - void ipsec_bindump(buf, len) caddr_t buf; int len; { int i; for (i = 0; i < len; i++) printf("%c", (unsigned char)buf[i]); return; } void ipsec_hexdump(buf, len) caddr_t buf; int len; { int i; for (i = 0; i < len; i++) { if (i != 0 && i % 32 == 0) printf("\n"); if (i % 4 == 0) printf(" "); printf("%02x", (unsigned char)buf[i]); } +#if 0 + if (i % 32 != 0) printf("\n"); +#endif return; } +#endif /* !defined(_KERNEL) || (defined(_KERNEL) && defined(IPSEC_DEBUG)) */ Index: head/sys/netkey/key_debug.h =================================================================== --- head/sys/netkey/key_debug.h (revision 62586) +++ head/sys/netkey/key_debug.h (revision 62587) @@ -1,89 +1,88 @@ +/* $FreeBSD$ */ +/* $KAME: key_debug.h,v 1.7 2000/07/04 04:08:16 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ -/* $Id: key_debug.h,v 1.1.6.2.6.1 1999/05/17 17:03:16 itojun Exp $ */ - #ifndef _NETKEY_KEY_DEBUG_H_ -#define _NETKEY_KEY_DEBUG_H_ +#define _NETKEY_KEY_DEBUG_H_ /* debug flags */ -#define KEYDEBUG_STAMP 0x00000001 /* path */ -#define KEYDEBUG_DATA 0x00000002 /* data */ -#define KEYDEBUG_DUMP 0x00000004 /* dump */ +#define KEYDEBUG_STAMP 0x00000001 /* path */ +#define KEYDEBUG_DATA 0x00000002 /* data */ +#define KEYDEBUG_DUMP 0x00000004 /* dump */ -#define KEYDEBUG_KEY 0x00000010 /* key processing */ -#define KEYDEBUG_ALG 0x00000020 /* ciph & auth algorithm */ -#define KEYDEBUG_IPSEC 0x00000040 /* ipsec processing */ +#define KEYDEBUG_KEY 0x00000010 /* key processing */ +#define KEYDEBUG_ALG 0x00000020 /* ciph & auth algorithm */ +#define KEYDEBUG_IPSEC 0x00000040 /* ipsec processing */ -#define KEYDEBUG_KEY_STAMP (KEYDEBUG_KEY | KEYDEBUG_STAMP) -#define KEYDEBUG_KEY_DATA (KEYDEBUG_KEY | KEYDEBUG_DATA) -#define KEYDEBUG_KEY_DUMP (KEYDEBUG_KEY | KEYDEBUG_DUMP) -#define KEYDEBUG_ALG_STAMP (KEYDEBUG_ALG | KEYDEBUG_STAMP) -#define KEYDEBUG_ALG_DATA (KEYDEBUG_ALG | KEYDEBUG_DATA) -#define KEYDEBUG_ALG_DUMP (KEYDEBUG_ALG | KEYDEBUG_DUMP) -#define KEYDEBUG_IPSEC_STAMP (KEYDEBUG_IPSEC | KEYDEBUG_STAMP) -#define KEYDEBUG_IPSEC_DATA (KEYDEBUG_IPSEC | KEYDEBUG_DATA) -#define KEYDEBUG_IPSEC_DUMP (KEYDEBUG_IPSEC | KEYDEBUG_DUMP) +#define KEYDEBUG_KEY_STAMP (KEYDEBUG_KEY | KEYDEBUG_STAMP) +#define KEYDEBUG_KEY_DATA (KEYDEBUG_KEY | KEYDEBUG_DATA) +#define KEYDEBUG_KEY_DUMP (KEYDEBUG_KEY | KEYDEBUG_DUMP) +#define KEYDEBUG_ALG_STAMP (KEYDEBUG_ALG | KEYDEBUG_STAMP) +#define KEYDEBUG_ALG_DATA (KEYDEBUG_ALG | KEYDEBUG_DATA) +#define KEYDEBUG_ALG_DUMP (KEYDEBUG_ALG | KEYDEBUG_DUMP) +#define KEYDEBUG_IPSEC_STAMP (KEYDEBUG_IPSEC | KEYDEBUG_STAMP) +#define KEYDEBUG_IPSEC_DATA (KEYDEBUG_IPSEC | KEYDEBUG_DATA) +#define KEYDEBUG_IPSEC_DUMP (KEYDEBUG_IPSEC | KEYDEBUG_DUMP) -#define KEYDEBUG(lev,arg) if ((key_debug_level & (lev)) == (lev)) { arg; } +#define KEYDEBUG(lev,arg) if ((key_debug_level & (lev)) == (lev)) { arg; } #ifdef _KERNEL extern u_int32_t key_debug_level; -#endif +#endif /*_KERNEL*/ struct sadb_msg; struct sadb_ext; extern void kdebug_sadb __P((struct sadb_msg *)); extern void kdebug_sadb_x_policy __P((struct sadb_ext *)); #ifdef _KERNEL struct secpolicy; struct secpolicyindex; struct secasindex; struct secasvar; struct secreplay; struct mbuf; extern void kdebug_secpolicy __P((struct secpolicy *)); extern void kdebug_secpolicyindex __P((struct secpolicyindex *)); extern void kdebug_secasindex __P((struct secasindex *)); extern void kdebug_secasv __P((struct secasvar *)); extern void kdebug_mbufhdr __P((struct mbuf *)); extern void kdebug_mbuf __P((struct mbuf *)); -#endif +#endif /*_KERNEL*/ struct sockaddr; extern void kdebug_sockaddr __P((struct sockaddr *)); extern void ipsec_hexdump __P((caddr_t, int)); extern void ipsec_bindump __P((caddr_t, int)); #endif /* _NETKEY_KEY_DEBUG_H_ */ Index: head/sys/netkey/key_var.h =================================================================== --- head/sys/netkey/key_var.h (revision 62586) +++ head/sys/netkey/key_var.h (revision 62587) @@ -1,74 +1,66 @@ +/* $FreeBSD$ */ +/* $KAME: key_var.h,v 1.8 2000/05/24 17:28:23 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ #ifndef _NETKEY_KEY_VAR_H_ -#define _NETKEY_KEY_VAR_H_ +#define _NETKEY_KEY_VAR_H_ /* sysctl */ -#define KEYCTL_DEBUG_LEVEL 1 -#define KEYCTL_SPI_TRY 2 -#define KEYCTL_SPI_MIN_VALUE 3 -#define KEYCTL_SPI_MAX_VALUE 4 -#define KEYCTL_RANDOM_INT 5 -#define KEYCTL_LARVAL_LIFETIME 6 -#define KEYCTL_BLOCKACQ_COUNT 7 -#define KEYCTL_BLOCKACQ_LIFETIME 8 -#define KEYCTL_MAXID 9 +#define KEYCTL_DEBUG_LEVEL 1 +#define KEYCTL_SPI_TRY 2 +#define KEYCTL_SPI_MIN_VALUE 3 +#define KEYCTL_SPI_MAX_VALUE 4 +#define KEYCTL_RANDOM_INT 5 +#define KEYCTL_LARVAL_LIFETIME 6 +#define KEYCTL_BLOCKACQ_COUNT 7 +#define KEYCTL_BLOCKACQ_LIFETIME 8 +#define KEYCTL_MAXID 9 -#define _ARRAYLEN(p) (sizeof(p)/sizeof(p[0])) -#define _KEYLEN(key) ((u_int)((key)->sadb_key_bits >> 3)) -#define _KEYBITS(key) ((u_int)((key)->sadb_key_bits)) -#define _KEYBUF(key) ((caddr_t)((caddr_t)(key) + sizeof(struct sadb_key))) +#define KEYCTL_NAMES { \ + { 0, 0 }, \ + { "debug", CTLTYPE_INT }, \ + { "spi_try", CTLTYPE_INT }, \ + { "spi_min_value", CTLTYPE_INT }, \ + { "spi_max_value", CTLTYPE_INT }, \ + { "random_int", CTLTYPE_INT }, \ + { "larval_lifetime", CTLTYPE_INT }, \ + { "blockacq_count", CTLTYPE_INT }, \ + { "blockacq_lifetime", CTLTYPE_INT }, \ +} -#define _INADDR(in) ((struct sockaddr_in *)(in)) -#define _IN6ADDR(in6) ((struct sockaddr_in6 *)(in6)) -#define _SALENBYAF(family) \ - (((family) == AF_INET) ? \ - (u_int)sizeof(struct sockaddr_in) : \ - (u_int)sizeof(struct sockaddr_in6)) -#define _INALENBYAF(family) \ - (((family) == AF_INET) ? \ - (u_int)sizeof(struct in_addr) : \ - (u_int)sizeof(struct in6_addr)) -#define _INADDRBYSA(saddr) \ - ((((struct sockaddr *)(saddr))->sa_family == AF_INET) ? \ - (caddr_t)&((struct sockaddr_in *)(saddr))->sin_addr : \ - (caddr_t)&((struct sockaddr_in6 *)(saddr))->sin6_addr) -#define _INPORTBYSA(saddr) \ - ((((struct sockaddr *)(saddr))->sa_family == AF_INET) ? \ - ((struct sockaddr_in *)(saddr))->sin_port : \ - ((struct sockaddr_in6 *)(saddr))->sin6_port) - -#ifdef SYSCTL_DECL -SYSCTL_DECL(_net_key); -#endif +#ifdef _KERNEL +#define _ARRAYLEN(p) (sizeof(p)/sizeof(p[0])) +#define _KEYLEN(key) ((u_int)((key)->sadb_key_bits >> 3)) +#define _KEYBITS(key) ((u_int)((key)->sadb_key_bits)) +#define _KEYBUF(key) ((caddr_t)((caddr_t)(key) + sizeof(struct sadb_key))) +#endif /*_KERNEL*/ #endif /* _NETKEY_KEY_VAR_H_ */ Index: head/sys/netkey/keydb.c =================================================================== --- head/sys/netkey/keydb.c (nonexistent) +++ head/sys/netkey/keydb.c (revision 62587) @@ -0,0 +1,217 @@ +/* $FreeBSD$ */ +/* $KAME: keydb.c,v 1.64 2000/05/11 17:02:30 itojun Exp $ */ + +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "opt_inet.h" +#include "opt_inet6.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include + +#include + +MALLOC_DEFINE(M_SECA, "key mgmt", "security associations, key management"); + +static void keydb_delsecasvar __P((struct secasvar *)); + +/* + * secpolicy management + */ +struct secpolicy * +keydb_newsecpolicy() +{ + struct secpolicy *p; + + p = (struct secpolicy *)malloc(sizeof(*p), M_SECA, M_NOWAIT); + if (!p) + return p; + bzero(p, sizeof(*p)); + return p; +} + +void +keydb_delsecpolicy(p) + struct secpolicy *p; +{ + + free(p, M_SECA); +} + +/* + * secashead management + */ +struct secashead * +keydb_newsecashead() +{ + struct secashead *p; + int i; + + p = (struct secashead *)malloc(sizeof(*p), M_SECA, M_NOWAIT); + if (!p) + return p; + bzero(p, sizeof(*p)); + for (i = 0; i < sizeof(p->savtree)/sizeof(p->savtree[0]); i++) + LIST_INIT(&p->savtree[i]); + return p; +} + +void +keydb_delsecashead(p) + struct secashead *p; +{ + + free(p, M_SECA); +} + +/* + * secasvar management (reference counted) + */ +struct secasvar * +keydb_newsecasvar() +{ + struct secasvar *p; + + p = (struct secasvar *)malloc(sizeof(*p), M_SECA, M_NOWAIT); + if (!p) + return p; + bzero(p, sizeof(*p)); + p->refcnt = 1; + return p; +} + +void +keydb_refsecasvar(p) + struct secasvar *p; +{ + int s; + + s = splnet(); + p->refcnt++; + splx(s); +} + +void +keydb_freesecasvar(p) + struct secasvar *p; +{ + int s; + + s = splnet(); + p->refcnt--; + /* negative refcnt will cause panic intentionally */ + if (p->refcnt <= 0) + keydb_delsecasvar(p); + splx(s); +} + +static void +keydb_delsecasvar(p) + struct secasvar *p; +{ + + if (p->refcnt) + panic("keydb_delsecasvar called with refcnt != 0"); + + free(p, M_SECA); +} + +/* + * secreplay management + */ +struct secreplay * +keydb_newsecreplay(wsize) + size_t wsize; +{ + struct secreplay *p; + + p = (struct secreplay *)malloc(sizeof(*p), M_SECA, M_NOWAIT); + if (!p) + return p; + + bzero(p, sizeof(*p)); + if (wsize != 0) { + p->bitmap = (caddr_t)malloc(wsize, M_SECA, M_NOWAIT); + if (!p->bitmap) { + free(p, M_SECA); + return NULL; + } + bzero(p->bitmap, wsize); + } + p->wsize = wsize; + return p; +} + +void +keydb_delsecreplay(p) + struct secreplay *p; +{ + + if (p->bitmap) + free(p->bitmap, M_SECA); + free(p, M_SECA); +} + +/* + * secreg management + */ +struct secreg * +keydb_newsecreg() +{ + struct secreg *p; + + p = (struct secreg *)malloc(sizeof(*p), M_SECA, M_NOWAIT); + if (p) + bzero(p, sizeof(*p)); + return p; +} + +void +keydb_delsecreg(p) + struct secreg *p; +{ + + free(p, M_SECA); +} Property changes on: head/sys/netkey/keydb.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: head/sys/netkey/keydb.h =================================================================== --- head/sys/netkey/keydb.h (revision 62586) +++ head/sys/netkey/keydb.h (revision 62587) @@ -1,135 +1,163 @@ +/* $FreeBSD$ */ +/* $KAME: keydb.h,v 1.11 2000/06/15 12:20:50 sakane Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ #ifndef _NETKEY_KEYDB_H_ -#define _NETKEY_KEYDB_H_ +#define _NETKEY_KEYDB_H_ #ifdef _KERNEL +#include + /* Security Assocciation Index */ -/* NOTE: Encure to be same address family */ +/* NOTE: Ensure to be same address family */ struct secasindex { - struct sockaddr_storage src; /* srouce address for SA */ - struct sockaddr_storage dst; /* destination address for SA */ - u_int16_t proto; /* IPPROTO_ESP or IPPROTO_AH */ - u_int8_t mode; /* mode of protocol, see ipsec.h */ + struct sockaddr_storage src; /* srouce address for SA */ + struct sockaddr_storage dst; /* destination address for SA */ + u_int16_t proto; /* IPPROTO_ESP or IPPROTO_AH */ + u_int8_t mode; /* mode of protocol, see ipsec.h */ + u_int32_t reqid; /* reqid id who owned this SA */ + /* see IPSEC_MANUAL_REQID_MAX. */ }; /* Security Association Data Base */ struct secashead { LIST_ENTRY(secashead) chain; - struct secasindex saidx; - struct secpolicyindex *owner; /* Indicate it who owned its SA. */ - /* If NULL then it's shared SA */ + struct secasindex saidx; - u_int8_t state; /* MATURE or DEAD. */ + struct sadb_ident *idents; /* source identity */ + struct sadb_ident *identd; /* destination identity */ + /* XXX I don't know how to use them. */ + + u_int8_t state; /* MATURE or DEAD. */ LIST_HEAD(_satree, secasvar) savtree[SADB_SASTATE_MAX+1]; /* SA chain */ /* The first of this list is newer SA */ - struct route sa_route; /* XXX */ + struct route sa_route; /* route cache */ }; /* Security Association */ struct secasvar { LIST_ENTRY(secasvar) chain; - int refcnt; /* reference count */ - u_int8_t state; /* Status of this Association */ + int refcnt; /* reference count */ + u_int8_t state; /* Status of this Association */ - u_int8_t alg_auth; /* Authentication Algorithm Identifier*/ - u_int8_t alg_enc; /* Cipher Algorithm Identifier */ - u_int32_t spi; /* SPI Value, network byte order */ - u_int32_t flags; /* holder for SADB_KEY_FLAGS */ + u_int8_t alg_auth; /* Authentication Algorithm Identifier*/ + u_int8_t alg_enc; /* Cipher Algorithm Identifier */ + u_int32_t spi; /* SPI Value, network byte order */ + u_int32_t flags; /* holder for SADB_KEY_FLAGS */ - struct sadb_key *key_auth; /* Key for Authentication */ - /* length has been shifted up to 3. */ - struct sadb_key *key_enc; /* Key for Encryption */ - /* length has been shifted up to 3. */ - caddr_t iv; /* Initilization Vector */ - u_int ivlen; /* length of IV */ + struct sadb_key *key_auth; /* Key for Authentication */ + struct sadb_key *key_enc; /* Key for Encryption */ + caddr_t iv; /* Initilization Vector */ + u_int ivlen; /* length of IV */ +#if 0 + caddr_t misc1; + caddr_t misc2; + caddr_t misc3; +#endif - struct secreplay *replay; /* replay prevention */ - u_int32_t tick; /* for lifetime */ + struct secreplay *replay; /* replay prevention */ + u_int32_t tick; /* for lifetime */ - struct sadb_lifetime *lft_c; /* CURRENT lifetime, it's constant. */ - struct sadb_lifetime *lft_h; /* HARD lifetime */ - struct sadb_lifetime *lft_s; /* SOFT lifetime */ + struct sadb_lifetime *lft_c; /* CURRENT lifetime, it's constant. */ + struct sadb_lifetime *lft_h; /* HARD lifetime */ + struct sadb_lifetime *lft_s; /* SOFT lifetime */ - u_int32_t seq; /* sequence number */ - pid_t pid; /* message's pid */ + u_int32_t seq; /* sequence number */ + pid_t pid; /* message's pid */ - struct secashead *sah; /* back pointer to the secashead */ + struct secashead *sah; /* back pointer to the secashead */ }; /* replay prevention */ struct secreplay { - u_int32_t count; - u_int wsize; /* window size, i.g. 4 bytes */ - u_int32_t seq; /* used by sender */ - u_int32_t lastseq; /* used by receiver */ - caddr_t bitmap; /* used by receiver */ + u_int32_t count; + u_int wsize; /* window size, i.g. 4 bytes */ + u_int32_t seq; /* used by sender */ + u_int32_t lastseq; /* used by receiver */ + caddr_t bitmap; /* used by receiver */ + int overflow; /* overflow flag */ }; /* socket table due to send PF_KEY messages. */ struct secreg { LIST_ENTRY(secreg) chain; - struct socket *so; + struct socket *so; }; #ifndef IPSEC_NONBLOCK_ACQUIRE /* acquiring list table. */ struct secacq { LIST_ENTRY(secacq) chain; - struct secasindex saidx; + struct secasindex saidx; - u_int32_t seq; /* sequence number */ - u_int32_t tick; /* for lifetime */ - int count; /* for lifetime */ + u_int32_t seq; /* sequence number */ + u_int32_t tick; /* for lifetime */ + int count; /* for lifetime */ }; #endif /* Sensitivity Level Specification */ /* nothing */ -#define SADB_KILL_INTERVAL 600 /* six seconds */ +#define SADB_KILL_INTERVAL 600 /* six seconds */ struct key_cb { int key_count; int any_count; }; + +/* secpolicy */ +extern struct secpolicy *keydb_newsecpolicy __P((void)); +extern void keydb_delsecpolicy __P((struct secpolicy *)); +/* secashead */ +extern struct secashead *keydb_newsecashead __P((void)); +extern void keydb_delsecashead __P((struct secashead *)); +/* secasvar */ +extern struct secasvar *keydb_newsecasvar __P((void)); +extern void keydb_refsecasvar __P((struct secasvar *)); +extern void keydb_freesecasvar __P((struct secasvar *)); +/* secreplay */ +extern struct secreplay *keydb_newsecreplay __P((size_t)); +extern void keydb_delsecreplay __P((struct secreplay *)); +/* secreg */ +extern struct secreg *keydb_newsecreg __P((void)); +extern void keydb_delsecreg __P((struct secreg *)); #endif /* _KERNEL */ #endif /* _NETKEY_KEYDB_H_ */ Index: head/sys/netkey/keysock.c =================================================================== --- head/sys/netkey/keysock.c (revision 62586) +++ head/sys/netkey/keysock.c (revision 62587) @@ -1,548 +1,615 @@ +/* $FreeBSD$ */ +/* $KAME: keysock.c,v 1.22 2000/05/23 13:19:21 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ -/* KAME @(#)$Id: keysock.c,v 1.2 1999/08/16 19:30:36 shin Exp $ */ +#include "opt_ipsec.h" /* This code has derived from sys/net/rtsock.c on FreeBSD2.2.5 */ -#include - #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef IPSEC_DEBUG #include #else #define KEYDEBUG(lev,arg) #endif #include -static MALLOC_DEFINE(M_SECA, "key mgmt", - "security associations, key management"); - struct sockaddr key_dst = { 2, PF_KEY, }; struct sockaddr key_src = { 2, PF_KEY, }; -struct sockproto key_proto = { PF_KEY, PF_KEY_V2 }; static int key_sendup0 __P((struct rawcb *, struct mbuf *, int)); -#define KMALLOC(p, t, n) \ - ((p) = (t) malloc((unsigned long)(n), M_SECA, M_NOWAIT)) -#define KFREE(p) \ - free((caddr_t)(p), M_SECA); +struct pfkeystat pfkeystat; /* + * key_output() + */ +int +#if __STDC__ +key_output(struct mbuf *m, ...) +#else +key_output(m, va_alist) + struct mbuf *m; + va_dcl +#endif +{ + struct sadb_msg *msg; + int len, error = 0; + int s; + struct socket *so; + va_list ap; + + va_start(ap, m); + so = va_arg(ap, struct socket *); + va_end(ap); + + if (m == 0) + panic("key_output: NULL pointer was passed.\n"); + + pfkeystat.out_total++; + pfkeystat.out_bytes += m->m_pkthdr.len; + + len = m->m_pkthdr.len; + if (len < sizeof(struct sadb_msg)) { +#ifdef IPSEC_DEBUG + printf("key_output: Invalid message length.\n"); +#endif + pfkeystat.out_tooshort++; + error = EINVAL; + goto end; + } + + if (m->m_len < sizeof(struct sadb_msg)) { + if ((m = m_pullup(m, sizeof(struct sadb_msg))) == 0) { +#ifdef IPSEC_DEBUG + printf("key_output: can't pullup mbuf\n"); +#endif + pfkeystat.out_nomem++; + error = ENOBUFS; + goto end; + } + } + + if ((m->m_flags & M_PKTHDR) == 0) + panic("key_output: not M_PKTHDR ??"); + +#ifdef IPSEC_DEBUG + KEYDEBUG(KEYDEBUG_KEY_DUMP, kdebug_mbuf(m)); +#endif + + msg = mtod(m, struct sadb_msg *); + pfkeystat.out_msgtype[msg->sadb_msg_type]++; + if (len != PFKEY_UNUNIT64(msg->sadb_msg_len)) { +#ifdef IPSEC_DEBUG + printf("key_output: Invalid message length.\n"); +#endif + pfkeystat.out_invlen++; + error = EINVAL; + goto end; + } + + /*XXX giant lock*/ + s = splnet(); + error = key_parse(m, so); + m = NULL; + splx(s); +end: + if (m) + m_freem(m); + return error; +} + +/* + * send message to the socket. + */ +static int +key_sendup0(rp, m, promisc) + struct rawcb *rp; + struct mbuf *m; + int promisc; +{ + if (promisc) { + struct sadb_msg *pmsg; + + M_PREPEND(m, sizeof(struct sadb_msg), M_NOWAIT); + if (m && m->m_len < sizeof(struct sadb_msg)) + m = m_pullup(m, sizeof(struct sadb_msg)); + if (!m) { +#ifdef IPSEC_DEBUG + printf("key_sendup0: cannot pullup\n"); +#endif + pfkeystat.in_nomem++; + m_freem(m); + return ENOBUFS; + } + m->m_pkthdr.len += sizeof(*pmsg); + + pmsg = mtod(m, struct sadb_msg *); + bzero(pmsg, sizeof(*pmsg)); + pmsg->sadb_msg_version = PF_KEY_V2; + pmsg->sadb_msg_type = SADB_X_PROMISC; + pmsg->sadb_msg_len = PFKEY_UNIT64(m->m_pkthdr.len); + /* pid and seq? */ + + pfkeystat.in_msgtype[pmsg->sadb_msg_type]++; + } + + if (!sbappendaddr(&rp->rcb_socket->so_rcv, + (struct sockaddr *)&key_src, m, NULL)) { +#ifdef IPSEC_DEBUG + printf("key_sendup0: sbappendaddr failed\n"); +#endif + pfkeystat.in_nomem++; + m_freem(m); + return ENOBUFS; + } + sorwakeup(rp->rcb_socket); + return 0; +} + +/* XXX this interface should be obsoleted. */ +int +key_sendup(so, msg, len, target) + struct socket *so; + struct sadb_msg *msg; + u_int len; + int target; /*target of the resulting message*/ +{ + struct mbuf *m, *n, *mprev; + int tlen; + + /* sanity check */ + if (so == 0 || msg == 0) + panic("key_sendup: NULL pointer was passed.\n"); + + KEYDEBUG(KEYDEBUG_KEY_DUMP, + printf("key_sendup: \n"); + kdebug_sadb(msg)); + + /* + * we increment statistics here, just in case we have ENOBUFS + * in this function. + */ + pfkeystat.in_total++; + pfkeystat.in_bytes += len; + pfkeystat.in_msgtype[msg->sadb_msg_type]++; + + /* + * Get mbuf chain whenever possible (not clusters), + * to save socket buffer. We'll be generating many SADB_ACQUIRE + * messages to listening key sockets. If we simply allocate clusters, + * sbappendaddr() will raise ENOBUFS due to too little sbspace(). + * sbspace() computes # of actual data bytes AND mbuf region. + * + * TODO: SADB_ACQUIRE filters should be implemented. + */ + tlen = len; + m = mprev = NULL; + while (tlen > 0) { + if (tlen == len) { + MGETHDR(n, M_DONTWAIT, MT_DATA); + n->m_len = MHLEN; + } else { + MGET(n, M_DONTWAIT, MT_DATA); + n->m_len = MLEN; + } + if (!n) { + pfkeystat.in_nomem++; + return ENOBUFS; + } + if (tlen >= MCLBYTES) { /*XXX better threshold? */ + MCLGET(n, M_DONTWAIT); + if ((n->m_flags & M_EXT) == 0) { + m_free(n); + m_freem(m); + pfkeystat.in_nomem++; + return ENOBUFS; + } + n->m_len = MCLBYTES; + } + + if (tlen < n->m_len) + n->m_len = tlen; + n->m_next = NULL; + if (m == NULL) + m = mprev = n; + else { + mprev->m_next = n; + mprev = n; + } + tlen -= n->m_len; + n = NULL; + } + m->m_pkthdr.len = len; + m->m_pkthdr.rcvif = NULL; + m_copyback(m, 0, len, (caddr_t)msg); + + /* avoid duplicated statistics */ + pfkeystat.in_total--; + pfkeystat.in_bytes -= len; + pfkeystat.in_msgtype[msg->sadb_msg_type]--; + + return key_sendup_mbuf(so, m, target); +} + +/* so can be NULL if target != KEY_SENDUP_ONE */ +int +key_sendup_mbuf(so, m, target) + struct socket *so; + struct mbuf *m; + int target; +{ + struct mbuf *n; + struct keycb *kp; + int sendup; + struct rawcb *rp; + int error = 0; + + if (m == NULL) + panic("key_sendup_mbuf: NULL pointer was passed.\n"); + if (so == NULL && target == KEY_SENDUP_ONE) + panic("key_sendup_mbuf: NULL pointer was passed.\n"); + + pfkeystat.in_total++; + pfkeystat.in_bytes += m->m_pkthdr.len; + if (m->m_len < sizeof(struct sadb_msg)) { +#if 1 + m = m_pullup(m, sizeof(struct sadb_msg)); + if (m == NULL) { + pfkeystat.in_nomem++; + return ENOBUFS; + } +#else + /* don't bother pulling it up just for stats */ +#endif + } + if (m->m_len >= sizeof(struct sadb_msg)) { + struct sadb_msg *msg; + msg = mtod(m, struct sadb_msg *); + pfkeystat.in_msgtype[msg->sadb_msg_type]++; + } + + LIST_FOREACH(rp, &rawcb_list, list) + { + if (rp->rcb_proto.sp_family != PF_KEY) + continue; + if (rp->rcb_proto.sp_protocol + && rp->rcb_proto.sp_protocol != PF_KEY_V2) { + continue; + } + + kp = (struct keycb *)rp; + + /* + * If you are in promiscuous mode, and when you get broadcasted + * reply, you'll get two PF_KEY messages. + * (based on pf_key@inner.net message on 14 Oct 1998) + */ + if (((struct keycb *)rp)->kp_promisc) { + if ((n = m_copy(m, 0, (int)M_COPYALL)) != NULL) { + (void)key_sendup0(rp, n, 1); + n = NULL; + } + } + + /* the exact target will be processed later */ + if (so && sotorawcb(so) == rp) + continue; + + sendup = 0; + switch (target) { + case KEY_SENDUP_ONE: + /* the statement has no effect */ + if (so && sotorawcb(so) == rp) + sendup++; + break; + case KEY_SENDUP_ALL: + sendup++; + break; + case KEY_SENDUP_REGISTERED: + if (kp->kp_registered) + sendup++; + break; + } + pfkeystat.in_msgtarget[target]++; + + if (!sendup) + continue; + + if ((n = m_copy(m, 0, (int)M_COPYALL)) == NULL) { +#ifdef IPSEC_DEBUG + printf("key_sendup: m_copy fail\n"); +#endif + m_freem(m); + pfkeystat.in_nomem++; + return ENOBUFS; + } + + if ((error = key_sendup0(rp, n, 0)) != 0) { + m_freem(m); + return error; + } + + n = NULL; + } + + if (so) { + error = key_sendup0(sotorawcb(so), m, 0); + m = NULL; + } else { + error = 0; + m_freem(m); + } + return error; +} + +/* * key_abort() * derived from net/rtsock.c:rts_abort() */ static int key_abort(struct socket *so) { int s, error; s = splnet(); error = raw_usrreqs.pru_abort(so); splx(s); return error; } /* * key_attach() * derived from net/rtsock.c:rts_attach() */ static int key_attach(struct socket *so, int proto, struct proc *p) { struct keycb *kp; int s, error; if (sotorawcb(so) != 0) return EISCONN; /* XXX panic? */ - MALLOC(kp, struct keycb *, sizeof *kp, M_PCB, M_WAITOK); /* XXX */ + kp = (struct keycb *)malloc(sizeof *kp, M_PCB, M_WAITOK); /* XXX */ if (kp == 0) return ENOBUFS; bzero(kp, sizeof *kp); /* * The splnet() is necessary to block protocols from sending * error notifications (like RTM_REDIRECT or RTM_LOSING) while * this PCB is extant but incompletely initialized. * Probably we should try to do more of this work beforehand and * eliminate the spl. */ s = splnet(); so->so_pcb = (caddr_t)kp; error = raw_usrreqs.pru_attach(so, proto, p); kp = (struct keycb *)sotorawcb(so); if (error) { free(kp, M_PCB); so->so_pcb = (caddr_t) 0; splx(s); printf("key_usrreq: key_usrreq results %d\n", error); return error; } kp->kp_promisc = kp->kp_registered = 0; if (kp->kp_raw.rcb_proto.sp_protocol == PF_KEY) /* XXX: AF_KEY */ key_cb.key_count++; key_cb.any_count++; kp->kp_raw.rcb_laddr = &key_src; kp->kp_raw.rcb_faddr = &key_dst; soisconnected(so); so->so_options |= SO_USELOOPBACK; splx(s); return 0; } /* * key_bind() * derived from net/rtsock.c:rts_bind() */ static int key_bind(struct socket *so, struct sockaddr *nam, struct proc *p) { int s, error; s = splnet(); error = raw_usrreqs.pru_bind(so, nam, p); /* xxx just EINVAL */ splx(s); return error; } /* * key_connect() * derived from net/rtsock.c:rts_connect() */ static int key_connect(struct socket *so, struct sockaddr *nam, struct proc *p) { int s, error; s = splnet(); error = raw_usrreqs.pru_connect(so, nam, p); /* XXX just EINVAL */ splx(s); return error; } /* * key_detach() * derived from net/rtsock.c:rts_detach() */ static int key_detach(struct socket *so) { struct keycb *kp = (struct keycb *)sotorawcb(so); int s, error; s = splnet(); if (kp != 0) { if (kp->kp_raw.rcb_proto.sp_protocol == PF_KEY) /* XXX: AF_KEY */ key_cb.key_count--; key_cb.any_count--; key_freereg(so); } error = raw_usrreqs.pru_detach(so); splx(s); return error; } /* * key_disconnect() * derived from net/rtsock.c:key_disconnect() */ static int key_disconnect(struct socket *so) { int s, error; s = splnet(); error = raw_usrreqs.pru_disconnect(so); splx(s); return error; } /* * key_peeraddr() * derived from net/rtsock.c:rts_peeraddr() */ static int key_peeraddr(struct socket *so, struct sockaddr **nam) { int s, error; s = splnet(); error = raw_usrreqs.pru_peeraddr(so, nam); splx(s); return error; } /* * key_send() * derived from net/rtsock.c:rts_send() */ static int key_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam, struct mbuf *control, struct proc *p) { int s, error; s = splnet(); error = raw_usrreqs.pru_send(so, flags, m, nam, control, p); splx(s); return error; } /* * key_shutdown() * derived from net/rtsock.c:rts_shutdown() */ static int key_shutdown(struct socket *so) { int s, error; s = splnet(); error = raw_usrreqs.pru_shutdown(so); splx(s); return error; } /* * key_sockaddr() * derived from net/rtsock.c:rts_sockaddr() */ static int key_sockaddr(struct socket *so, struct sockaddr **nam) { int s, error; s = splnet(); error = raw_usrreqs.pru_sockaddr(so, nam); splx(s); return error; } struct pr_usrreqs key_usrreqs = { key_abort, pru_accept_notsupp, key_attach, key_bind, key_connect, pru_connect2_notsupp, pru_control_notsupp, key_detach, key_disconnect, pru_listen_notsupp, key_peeraddr, pru_rcvd_notsupp, pru_rcvoob_notsupp, key_send, pru_sense_null, key_shutdown, key_sockaddr, sosend, soreceive, sopoll }; - -/* - * key_output() - */ -int -key_output(struct mbuf *m, struct socket *so) -{ - struct sadb_msg *msg = NULL; - int len, error = 0; - int s; - int target; - - if (m == 0) - panic("key_output: NULL pointer was passed.\n"); - - if (m->m_len < sizeof(long) - && (m = m_pullup(m, 8)) == 0) { - printf("key_output: can't pullup mbuf\n"); - error = ENOBUFS; - goto end; - } - - if ((m->m_flags & M_PKTHDR) == 0) - panic("key_output: not M_PKTHDR ??"); - -#if defined(IPSEC_DEBUG) - KEYDEBUG(KEYDEBUG_KEY_DUMP, kdebug_mbuf(m)); -#endif /* defined(IPSEC_DEBUG) */ - - len = m->m_pkthdr.len; - if (len < sizeof(struct sadb_msg) - || len != PFKEY_UNUNIT64(mtod(m, struct sadb_msg *)->sadb_msg_len)) { - printf("key_output: Invalid message length.\n"); - error = EINVAL; - goto end; - } - - /* - * allocate memory for sadb_msg, and copy to sadb_msg from mbuf - * XXX: To be processed directly without a copy. - */ - KMALLOC(msg, struct sadb_msg *, len); - if (msg == 0) { - printf("key_output: No more memory.\n"); - error = ENOBUFS; - goto end; - /* or do panic ? */ - } - m_copydata(m, 0, len, (caddr_t)msg); - - /*XXX giant lock*/ - s = splnet(); - if ((len = key_parse(&msg, so, &target)) == 0) { - /* discard. i.e. no need to reply. */ - error = 0; - splx(s); - goto end; - } - - /* send up message to the socket */ - error = key_sendup(so, msg, len, target); - splx(s); - KFREE(msg); -end: - m_freem(m); - return (error); -} - -/* - * send message to the socket. - */ -static int -key_sendup0(rp, m, promisc) - struct rawcb *rp; - struct mbuf *m; - int promisc; -{ - if (promisc) { - struct sadb_msg *pmsg; - - M_PREPEND(m, sizeof(struct sadb_msg), M_NOWAIT); - if (m && m->m_len < sizeof(struct sadb_msg)) - m = m_pullup(m, sizeof(struct sadb_msg)); - if (!m) { - printf("key_sendup0: cannot pullup\n"); - m_freem(m); - return ENOBUFS; - } - m->m_pkthdr.len += sizeof(*pmsg); - - pmsg = mtod(m, struct sadb_msg *); - bzero(pmsg, sizeof(*pmsg)); - pmsg->sadb_msg_version = PF_KEY_V2; - pmsg->sadb_msg_type = SADB_X_PROMISC; - pmsg->sadb_msg_len = PFKEY_UNIT64(m->m_pkthdr.len); - /* pid and seq? */ - } - - if (!sbappendaddr(&rp->rcb_socket->so_rcv, - (struct sockaddr *)&key_src, m, NULL)) { - printf("key_sendup0: sbappendaddr failed\n"); - m_freem(m); - return ENOBUFS; - } - sorwakeup(rp->rcb_socket); - return 0; -} - -int -key_sendup(so, msg, len, target) - struct socket *so; - struct sadb_msg *msg; - u_int len; - int target; /*target of the resulting message*/ -{ - struct mbuf *m, *n, *mprev; - struct keycb *kp; - int sendup; - struct rawcb *rp; - int error; - int tlen; - - /* sanity check */ - if (so == 0 || msg == 0) - panic("key_sendup: NULL pointer was passed.\n"); - - KEYDEBUG(KEYDEBUG_KEY_DUMP, - printf("key_sendup: \n"); - kdebug_sadb(msg)); - - /* - * Get mbuf chain whenever possible (not clusters), - * to save socket buffer. We'll be generating many SADB_ACQUIRE - * messages to listening key sockets. If we simmply allocate clusters, - * sbappendaddr() will raise ENOBUFS due to too little sbspace(). - * sbspace() computes # of actual data bytes AND mbuf region. - * - * TODO: SADB_ACQUIRE filters should be implemented. - */ - tlen = len; - m = mprev = NULL; - while (tlen > 0) { - if (tlen == len) { - MGETHDR(n, M_DONTWAIT, MT_DATA); - if (n == NULL) { - m_freem(m); - return ENOBUFS; - } - n->m_len = MHLEN; - } else { - MGET(n, M_DONTWAIT, MT_DATA); - if (n == NULL) { - m_freem(m); - return ENOBUFS; - } - n->m_len = MLEN; - } - if (tlen > MCLBYTES) { /*XXX better threshold? */ - MCLGET(n, M_DONTWAIT); - if ((n->m_flags & M_EXT) == 0) { - m_free(n); - m_freem(m); - return ENOBUFS; - } - n->m_len = MCLBYTES; - } - - if (tlen < n->m_len) - n->m_len = tlen; - n->m_next = NULL; - if (m == NULL) - m = mprev = n; - else { - mprev->m_next = n; - mprev = n; - } - tlen -= n->m_len; - n = NULL; - } - m->m_pkthdr.len = len; - m->m_pkthdr.rcvif = NULL; - m_copyback(m, 0, len, (caddr_t)msg); - - LIST_FOREACH(rp, &rawcb_list, list) - { - if (rp->rcb_proto.sp_family != PF_KEY) - continue; - if (rp->rcb_proto.sp_protocol - && rp->rcb_proto.sp_protocol != PF_KEY_V2) { - continue; - } - - kp = (struct keycb *)rp; - - /* - * If you are in promiscuous mode, and when you get broadcasted - * reply, you'll get two PF_KEY messages. - * (based on pf_key@inner.net message on 14 Oct 1998) - */ - if (((struct keycb *)rp)->kp_promisc) { - if ((n = m_copy(m, 0, (int)M_COPYALL)) != NULL) { - (void)key_sendup0(rp, n, 1); - n = NULL; - } - } - - /* the exact target will be processed later */ - if (sotorawcb(so) == rp) - continue; - - sendup = 0; - switch (target) { - case KEY_SENDUP_ONE: - /* the statement has no effect */ - if (sotorawcb(so) == rp) - sendup++; - break; - case KEY_SENDUP_ALL: - sendup++; - break; - case KEY_SENDUP_REGISTERED: - if (kp->kp_registered) - sendup++; - break; - } - - if (!sendup) - continue; - - if ((n = m_copy(m, 0, (int)M_COPYALL)) == NULL) { - printf("key_sendup: m_copy fail\n"); - m_freem(m); - return ENOBUFS; - } - - if ((error = key_sendup0(rp, n, 0)) != 0) { - m_freem(m); - return error; - } - - n = NULL; - } - - error = key_sendup0(sotorawcb(so), m, 0); - m = NULL; - return error; -} /* sysctl */ SYSCTL_NODE(_net, PF_KEY, key, CTLFLAG_RW, 0, "Key Family"); /* * Definitions of protocols supported in the KEY domain. */ extern struct domain keydomain; struct protosw keysw[] = { { SOCK_RAW, &keydomain, PF_KEY_V2, PR_ATOMIC|PR_ADDR, 0, key_output, raw_ctlinput, 0, 0, raw_init, 0, 0, 0, &key_usrreqs } }; struct domain keydomain = { PF_KEY, "key", key_init, 0, 0, keysw, &keysw[sizeof(keysw)/sizeof(keysw[0])] }; DOMAIN_SET(key); Index: head/sys/netkey/keysock.h =================================================================== --- head/sys/netkey/keysock.h (revision 62586) +++ head/sys/netkey/keysock.h (revision 62587) @@ -1,56 +1,82 @@ +/* $FreeBSD$ */ +/* $KAME: keysock.h,v 1.8 2000/03/27 05:11:06 sumikawa Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ -/* $Id: keysock.h,v 1.1.6.3.6.1 1999/05/17 17:03:19 itojun Exp $ */ - #ifndef _NETKEY_KEYSOCK_H_ -#define _NETKEY_KEYSOCK_H_ +#define _NETKEY_KEYSOCK_H_ +/* statistics for pfkey socket */ +struct pfkeystat { + /* kernel -> userland */ + u_quad_t out_total; /* # of total calls */ + u_quad_t out_bytes; /* total bytecount */ + u_quad_t out_msgtype[256]; /* message type histogram */ + u_quad_t out_invlen; /* invalid length field */ + u_quad_t out_invver; /* invalid version field */ + u_quad_t out_invmsgtype; /* invalid message type field */ + u_quad_t out_tooshort; /* msg too short */ + u_quad_t out_nomem; /* memory allocation failure */ + u_quad_t out_dupext; /* duplicate extension */ + u_quad_t out_invexttype; /* invalid extension type */ + u_quad_t out_invsatype; /* invalid sa type */ + u_quad_t out_invaddr; /* invalid address extension */ + /* userland -> kernel */ + u_quad_t in_total; /* # of total calls */ + u_quad_t in_bytes; /* total bytecount */ + u_quad_t in_msgtype[256]; /* message type histogram */ + u_quad_t in_msgtarget[3]; /* one/all/registered */ + u_quad_t in_nomem; /* memory allocation failure */ + /* others */ + u_quad_t sockerr; /* # of socket related errors */ +}; + +#define KEY_SENDUP_ONE 0 +#define KEY_SENDUP_ALL 1 +#define KEY_SENDUP_REGISTERED 2 + #ifdef _KERNEL struct keycb { - struct rawcb kp_raw; /* rawcb */ - int kp_promisc; /* promiscuous mode */ - int kp_registered; /* registered socket */ + struct rawcb kp_raw; /* rawcb */ + int kp_promisc; /* promiscuous mode */ + int kp_registered; /* registered socket */ }; -extern int key_output __P((struct mbuf *, struct socket *)); -extern int key_usrreq __P((struct socket *, int, struct mbuf *, - struct mbuf *, struct mbuf *)); +extern struct pfkeystat pfkeystat; -#define KEY_SENDUP_ONE 0 -#define KEY_SENDUP_ALL 1 -#define KEY_SENDUP_REGISTERED 2 +extern int key_output __P((struct mbuf *, ...)); +extern int key_usrreq __P((struct socket *, + int, struct mbuf *, struct mbuf *, struct mbuf *)); -extern int key_sendup __P((struct socket *, struct sadb_msg *, u_int, - int)); +extern int key_sendup __P((struct socket *, struct sadb_msg *, u_int, int)); +extern int key_sendup_mbuf __P((struct socket *, struct mbuf *, int)); #endif /* _KERNEL */ -#endif /* _NETKEY_KEYSOCK_H_ */ +#endif /*_NETKEY_KEYSOCK_H_*/ Index: head/sys/sys/mbuf.h =================================================================== --- head/sys/sys/mbuf.h (revision 62586) +++ head/sys/sys/mbuf.h (revision 62587) @@ -1,562 +1,578 @@ /* * Copyright (c) 1982, 1986, 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)mbuf.h 8.5 (Berkeley) 2/19/95 * $FreeBSD$ */ #ifndef _SYS_MBUF_H_ #define _SYS_MBUF_H_ /* * Mbufs are of a single size, MSIZE (machine/param.h), which * includes overhead. An mbuf may add a single "mbuf cluster" of size * MCLBYTES (also in machine/param.h), which has no additional overhead * and is used instead of the internal data area; this is done when * at least MINCLSIZE of data must be stored. */ #define MLEN (MSIZE - sizeof(struct m_hdr)) /* normal data len */ #define MHLEN (MLEN - sizeof(struct pkthdr)) /* data len w/pkthdr */ #define MINCLSIZE (MHLEN + 1) /* smallest amount to put in cluster */ #define M_MAXCOMPRESS (MHLEN / 2) /* max amount to copy for compression */ /* * Macros for type conversion * mtod(m, t) - convert mbuf pointer to data pointer of correct type * dtom(x) - convert data pointer within mbuf to mbuf pointer (XXX) * mtocl(x) - convert pointer within cluster to cluster index # * cltom(x) - convert cluster # to ptr to beginning of cluster */ #define mtod(m, t) ((t)((m)->m_data)) #define dtom(x) ((struct mbuf *)((intptr_t)(x) & ~(MSIZE-1))) #define mtocl(x) (((uintptr_t)(x) - (uintptr_t)mbutl) >> MCLSHIFT) #define cltom(x) ((caddr_t)((uintptr_t)mbutl + \ ((uintptr_t)(x) << MCLSHIFT))) /* header at beginning of each mbuf: */ struct m_hdr { struct mbuf *mh_next; /* next buffer in chain */ struct mbuf *mh_nextpkt; /* next chain in queue/record */ caddr_t mh_data; /* location of data */ int mh_len; /* amount of data in this mbuf */ short mh_type; /* type of data in this mbuf */ short mh_flags; /* flags; see below */ }; /* record/packet header in first mbuf of chain; valid if M_PKTHDR set */ struct pkthdr { struct ifnet *rcvif; /* rcv interface */ int len; /* total packet length */ /* variables for ip and tcp reassembly */ - void *header; /* pointer to packet header */ + caddr_t header; /* pointer to packet header */ /* variables for hardware checksum */ int csum_flags; /* flags regarding checksum */ int csum_data; /* data field used by csum routines */ + struct mbuf *aux; /* extra data buffer; ipsec/others */ }; /* description of external storage mapped into mbuf, valid if M_EXT set */ struct m_ext { caddr_t ext_buf; /* start of buffer */ void (*ext_free) /* free routine if not the usual */ __P((caddr_t, u_int)); u_int ext_size; /* size of buffer, for ext_free */ void (*ext_ref) /* add a reference to the ext object */ __P((caddr_t, u_int)); }; struct mbuf { struct m_hdr m_hdr; union { struct { struct pkthdr MH_pkthdr; /* M_PKTHDR set */ union { struct m_ext MH_ext; /* M_EXT set */ char MH_databuf[MHLEN]; } MH_dat; } MH; char M_databuf[MLEN]; /* !M_PKTHDR, !M_EXT */ } M_dat; }; #define m_next m_hdr.mh_next #define m_len m_hdr.mh_len #define m_data m_hdr.mh_data #define m_type m_hdr.mh_type #define m_flags m_hdr.mh_flags #define m_nextpkt m_hdr.mh_nextpkt #define m_act m_nextpkt #define m_pkthdr M_dat.MH.MH_pkthdr #define m_ext M_dat.MH.MH_dat.MH_ext #define m_pktdat M_dat.MH.MH_dat.MH_databuf #define m_dat M_dat.M_databuf /* mbuf flags */ #define M_EXT 0x0001 /* has associated external storage */ #define M_PKTHDR 0x0002 /* start of record */ #define M_EOR 0x0004 /* end of record */ #define M_PROTO1 0x0008 /* protocol-specific */ #define M_PROTO2 0x0010 /* protocol-specific */ #define M_PROTO3 0x0020 /* protocol-specific */ #define M_PROTO4 0x0040 /* protocol-specific */ #define M_PROTO5 0x0080 /* protocol-specific */ /* mbuf pkthdr flags, also in m_flags */ #define M_BCAST 0x0100 /* send/received as link-level broadcast */ #define M_MCAST 0x0200 /* send/received as link-level multicast */ #define M_FRAG 0x0400 /* packet is a fragment of a larger packet */ #define M_FIRSTFRAG 0x0800 /* packet is first fragment */ #define M_LASTFRAG 0x1000 /* packet is last fragment */ /* flags copied when copying m_pkthdr */ #define M_COPYFLAGS (M_PKTHDR|M_EOR|M_PROTO1|M_PROTO1|M_PROTO2|M_PROTO3 | \ M_PROTO4|M_PROTO5|M_BCAST|M_MCAST|M_FRAG) /* flags indicating hw checksum support and sw checksum requirements */ #define CSUM_IP 0x0001 /* will csum IP */ #define CSUM_TCP 0x0002 /* will csum TCP */ #define CSUM_UDP 0x0004 /* will csum UDP */ #define CSUM_IP_FRAGS 0x0008 /* will csum IP fragments */ #define CSUM_FRAGMENT 0x0010 /* will do IP fragmentation */ #define CSUM_IP_CHECKED 0x0100 /* did csum IP */ #define CSUM_IP_VALID 0x0200 /* ... the csum is valid */ #define CSUM_DATA_VALID 0x0400 /* csum_data field is valid */ #define CSUM_PSEUDO_HDR 0x0800 /* csum_data has pseudo hdr */ #define CSUM_DELAY_DATA (CSUM_TCP | CSUM_UDP) #define CSUM_DELAY_IP (CSUM_IP) /* XXX add ipv6 here too? */ /* mbuf types */ #define MT_FREE 0 /* should be on free list */ #define MT_DATA 1 /* dynamic (data) allocation */ #define MT_HEADER 2 /* packet header */ #if 0 #define MT_SOCKET 3 /* socket structure */ #define MT_PCB 4 /* protocol control block */ #define MT_RTABLE 5 /* routing tables */ #define MT_HTABLE 6 /* IMP host tables */ #define MT_ATABLE 7 /* address resolution tables */ #endif #define MT_SONAME 8 /* socket name */ #if 0 #define MT_SOOPTS 10 /* socket options */ #endif #define MT_FTABLE 11 /* fragment reassembly header */ #if 0 #define MT_RIGHTS 12 /* access rights */ #define MT_IFADDR 13 /* interface address */ #endif #define MT_CONTROL 14 /* extra-data protocol message */ #define MT_OOBDATA 15 /* expedited data */ /* * mbuf statistics */ struct mbstat { u_long m_mbufs; /* mbufs obtained from page pool */ u_long m_clusters; /* clusters obtained from page pool */ u_long m_spare; /* spare field */ u_long m_clfree; /* free clusters */ u_long m_drops; /* times failed to find space */ u_long m_wait; /* times waited for space */ u_long m_drain; /* times drained protocols for space */ u_short m_mtypes[256]; /* type specific mbuf allocations */ u_long m_mcfail; /* times m_copym failed */ u_long m_mpfail; /* times m_pullup failed */ u_long m_msize; /* length of an mbuf */ u_long m_mclbytes; /* length of an mbuf cluster */ u_long m_minclsize; /* min length of data to allocate a cluster */ u_long m_mlen; /* length of data in an mbuf */ u_long m_mhlen; /* length of data in a header mbuf */ }; /* flags to m_get/MGET */ #define M_DONTWAIT 1 #define M_WAIT 0 /* Freelists: * * Normal mbuf clusters are normally treated as character arrays * after allocation, but use the first word of the buffer as a free list * pointer while on the free list. */ union mcluster { union mcluster *mcl_next; char mcl_buf[MCLBYTES]; }; /* * These are identifying numbers passed to the m_mballoc_wait function, * allowing us to determine whether the call came from an MGETHDR or * an MGET. */ #define MGETHDR_C 1 #define MGET_C 2 /* * Wake up the next instance (if any) of m_mballoc_wait() which is * waiting for an mbuf to be freed. This should be called at splimp(). * * XXX: If there is another free mbuf, this routine will be called [again] * from the m_mballoc_wait routine in order to wake another sleep instance. */ #define MMBWAKEUP() do { \ if (m_mballoc_wid) { \ m_mballoc_wid--; \ wakeup_one(&m_mballoc_wid); \ } \ } while (0) /* * Same as above, but for mbuf cluster(s). */ #define MCLWAKEUP() do { \ if (m_clalloc_wid) { \ m_clalloc_wid--; \ wakeup_one(&m_clalloc_wid); \ } \ } while (0) /* * mbuf utility macros: * * MBUFLOCK(code) * prevents a section of code from from being interrupted by network * drivers. */ #define MBUFLOCK(code) do { \ int _ms = splimp(); \ \ { code } \ splx(_ms); \ } while (0) /* * mbuf allocation/deallocation macros: * * MGET(struct mbuf *m, int how, int type) * allocates an mbuf and initializes it to contain internal data. * * MGETHDR(struct mbuf *m, int how, int type) * allocates an mbuf and initializes it to contain a packet header * and internal data. */ #define MGET(m, how, type) do { \ struct mbuf *_mm; \ int _mhow = (how); \ int _mtype = (type); \ int _ms = splimp(); \ \ if (mmbfree == NULL) \ (void)m_mballoc(1, _mhow); \ _mm = mmbfree; \ if (_mm != NULL) { \ mmbfree = _mm->m_next; \ mbstat.m_mtypes[MT_FREE]--; \ _mm->m_type = _mtype; \ mbstat.m_mtypes[_mtype]++; \ _mm->m_next = NULL; \ _mm->m_nextpkt = NULL; \ _mm->m_data = _mm->m_dat; \ _mm->m_flags = 0; \ (m) = _mm; \ splx(_ms); \ } else { \ splx(_ms); \ _mm = m_retry(_mhow, _mtype); \ if (_mm == NULL && _mhow == M_WAIT) \ (m) = m_mballoc_wait(MGET_C, _mtype); \ else \ (m) = _mm; \ } \ } while (0) #define MGETHDR(m, how, type) do { \ struct mbuf *_mm; \ int _mhow = (how); \ int _mtype = (type); \ int _ms = splimp(); \ \ if (mmbfree == NULL) \ (void)m_mballoc(1, _mhow); \ _mm = mmbfree; \ if (_mm != NULL) { \ mmbfree = _mm->m_next; \ mbstat.m_mtypes[MT_FREE]--; \ _mm->m_type = _mtype; \ mbstat.m_mtypes[_mtype]++; \ _mm->m_next = NULL; \ _mm->m_nextpkt = NULL; \ _mm->m_data = _mm->m_pktdat; \ _mm->m_flags = M_PKTHDR; \ _mm->m_pkthdr.rcvif = NULL; \ _mm->m_pkthdr.csum_flags = 0; \ + _mm->m_pkthdr.aux = (struct mbuf *)NULL; \ (m) = _mm; \ splx(_ms); \ } else { \ splx(_ms); \ _mm = m_retryhdr(_mhow, _mtype); \ if (_mm == NULL && _mhow == M_WAIT) \ (m) = m_mballoc_wait(MGETHDR_C, _mtype); \ else \ (m) = _mm; \ } \ } while (0) /* * Mbuf cluster macros. * MCLALLOC(caddr_t p, int how) allocates an mbuf cluster. * MCLGET adds such clusters to a normal mbuf; * the flag M_EXT is set upon success. * MCLFREE releases a reference to a cluster allocated by MCLALLOC, * freeing the cluster if the reference count has reached 0. */ #define MCLALLOC(p, how) do { \ caddr_t _mp; \ int _mhow = (how); \ int _ms = splimp(); \ \ if (mclfree == NULL) \ (void)m_clalloc(1, _mhow); \ _mp = (caddr_t)mclfree; \ if (_mp != NULL) { \ mclrefcnt[mtocl(_mp)]++; \ mbstat.m_clfree--; \ mclfree = ((union mcluster *)_mp)->mcl_next; \ (p) = _mp; \ splx(_ms); \ } else { \ splx(_ms); \ if (_mhow == M_WAIT) \ (p) = m_clalloc_wait(); \ else \ (p) = NULL; \ } \ } while (0) #define MCLGET(m, how) do { \ struct mbuf *_mm = (m); \ \ MCLALLOC(_mm->m_ext.ext_buf, (how)); \ if (_mm->m_ext.ext_buf != NULL) { \ _mm->m_data = _mm->m_ext.ext_buf; \ _mm->m_flags |= M_EXT; \ _mm->m_ext.ext_free = NULL; \ _mm->m_ext.ext_ref = NULL; \ _mm->m_ext.ext_size = MCLBYTES; \ } \ } while (0) #define MCLFREE1(p) do { \ union mcluster *_mp = (union mcluster *)(p); \ \ KASSERT(mclrefcnt[mtocl(_mp)] > 0, ("freeing free cluster")); \ if (--mclrefcnt[mtocl(_mp)] == 0) { \ _mp->mcl_next = mclfree; \ mclfree = _mp; \ mbstat.m_clfree++; \ MCLWAKEUP(); \ } \ } while (0) #define MCLFREE(p) MBUFLOCK( \ MCLFREE1(p); \ ) #define MEXTFREE1(m) do { \ struct mbuf *_mm = (m); \ \ if (_mm->m_ext.ext_free != NULL) \ (*_mm->m_ext.ext_free)(_mm->m_ext.ext_buf, \ _mm->m_ext.ext_size); \ else \ MCLFREE1(_mm->m_ext.ext_buf); \ } while (0) #define MEXTFREE(m) MBUFLOCK( \ MEXTFREE1(m); \ ) /* * MFREE(struct mbuf *m, struct mbuf *n) * Free a single mbuf and associated external storage. * Place the successor, if any, in n. */ #define MFREE(m, n) MBUFLOCK( \ struct mbuf *_mm = (m); \ \ KASSERT(_mm->m_type != MT_FREE, ("freeing free mbuf")); \ mbstat.m_mtypes[_mm->m_type]--; \ if (_mm->m_flags & M_EXT) \ MEXTFREE1(m); \ (n) = _mm->m_next; \ _mm->m_type = MT_FREE; \ mbstat.m_mtypes[MT_FREE]++; \ _mm->m_next = mmbfree; \ mmbfree = _mm; \ MMBWAKEUP(); \ ) /* * Copy mbuf pkthdr from "from" to "to". * from must have M_PKTHDR set, and to must be empty. + * aux pointer will be moved to `to'. */ #define M_COPY_PKTHDR(to, from) do { \ struct mbuf *_mfrom = (from); \ struct mbuf *_mto = (to); \ \ _mto->m_data = _mto->m_pktdat; \ _mto->m_flags = _mfrom->m_flags & M_COPYFLAGS; \ _mto->m_pkthdr = _mfrom->m_pkthdr; \ + _mfrom->m_pkthdr.aux = (struct mbuf *)NULL; \ } while (0) /* * Set the m_data pointer of a newly-allocated mbuf (m_get/MGET) to place * an object of the specified size at the end of the mbuf, longword aligned. */ #define M_ALIGN(m, len) do { \ (m)->m_data += (MLEN - (len)) & ~(sizeof(long) - 1); \ } while (0) /* * As above, for mbufs allocated with m_gethdr/MGETHDR * or initialized by M_COPY_PKTHDR. */ #define MH_ALIGN(m, len) do { \ (m)->m_data += (MHLEN - (len)) & ~(sizeof(long) - 1); \ } while (0) /* * Compute the amount of space available * before the current start of data in an mbuf. */ #define M_LEADINGSPACE(m) \ ((m)->m_flags & M_EXT ? \ /* (m)->m_data - (m)->m_ext.ext_buf */ 0 : \ (m)->m_flags & M_PKTHDR ? (m)->m_data - (m)->m_pktdat : \ (m)->m_data - (m)->m_dat) /* * Compute the amount of space available * after the end of data in an mbuf. */ #define M_TRAILINGSPACE(m) \ ((m)->m_flags & M_EXT ? (m)->m_ext.ext_buf + \ (m)->m_ext.ext_size - ((m)->m_data + (m)->m_len) : \ &(m)->m_dat[MLEN] - ((m)->m_data + (m)->m_len)) /* * Arrange to prepend space of size plen to mbuf m. * If a new mbuf must be allocated, how specifies whether to wait. * If how is M_DONTWAIT and allocation fails, the original mbuf chain * is freed and m is set to NULL. */ #define M_PREPEND(m, plen, how) do { \ struct mbuf **_mmp = &(m); \ struct mbuf *_mm = *_mmp; \ int _mplen = (plen); \ int __mhow = (how); \ \ if (M_LEADINGSPACE(_mm) >= _mplen) { \ _mm->m_data -= _mplen; \ _mm->m_len += _mplen; \ } else \ _mm = m_prepend(_mm, _mplen, __mhow); \ if (_mm != NULL && _mm->m_flags & M_PKTHDR) \ _mm->m_pkthdr.len += _mplen; \ *_mmp = _mm; \ } while (0) /* change mbuf to new type */ #define MCHTYPE(m, t) do { \ struct mbuf *_mm = (m); \ int _mt = (t); \ int _ms = splimp(); \ \ mbstat.m_mtypes[_mm->m_type]--; \ mbstat.m_mtypes[_mt]++; \ splx(_ms); \ _mm->m_type = (_mt); \ } while (0) /* length to m_copy to copy all */ #define M_COPYALL 1000000000 /* compatibility with 4.3 */ #define m_copy(m, o, l) m_copym((m), (o), (l), M_DONTWAIT) +/* + * pkthdr.aux type tags. + */ +struct mauxtag { + int af; + int type; +}; + #ifdef _KERNEL extern u_int m_clalloc_wid; /* mbuf cluster wait count */ extern u_int m_mballoc_wid; /* mbuf wait count */ extern int max_linkhdr; /* largest link-level header */ extern int max_protohdr; /* largest protocol header */ extern int max_hdr; /* largest link+protocol header */ extern int max_datalen; /* MHLEN - max_hdr */ extern struct mbstat mbstat; extern int mbuf_wait; /* mbuf sleep time */ extern struct mbuf *mbutl; /* virtual address of mclusters */ extern char *mclrefcnt; /* cluster reference counts */ extern union mcluster *mclfree; extern struct mbuf *mmbfree; extern int nmbclusters; extern int nmbufs; extern int nsfbufs; void m_adj __P((struct mbuf *, int)); void m_cat __P((struct mbuf *,struct mbuf *)); int m_clalloc __P((int, int)); caddr_t m_clalloc_wait __P((void)); void m_copyback __P((struct mbuf *, int, int, caddr_t)); void m_copydata __P((struct mbuf *,int,int,caddr_t)); struct mbuf *m_copym __P((struct mbuf *, int, int, int)); struct mbuf *m_copypacket __P((struct mbuf *, int)); struct mbuf *m_devget __P((char *, int, int, struct ifnet *, void (*copy)(char *, caddr_t, u_int))); struct mbuf *m_dup __P((struct mbuf *, int)); struct mbuf *m_free __P((struct mbuf *)); void m_freem __P((struct mbuf *)); struct mbuf *m_get __P((int, int)); struct mbuf *m_getclr __P((int, int)); struct mbuf *m_gethdr __P((int, int)); int m_mballoc __P((int, int)); struct mbuf *m_mballoc_wait __P((int, int)); struct mbuf *m_prepend __P((struct mbuf *,int,int)); +struct mbuf *m_pulldown __P((struct mbuf *, int, int, int *)); void m_print __P((const struct mbuf *m)); struct mbuf *m_pullup __P((struct mbuf *, int)); struct mbuf *m_retry __P((int, int)); struct mbuf *m_retryhdr __P((int, int)); struct mbuf *m_split __P((struct mbuf *,int,int)); +struct mbuf *m_aux_add __P((struct mbuf *, int, int)); +struct mbuf *m_aux_find __P((struct mbuf *, int, int)); +void m_aux_delete __P((struct mbuf *, struct mbuf *)); #endif /* _KERNEL */ #endif /* !_SYS_MBUF_H_ */ Index: head/sys/sys/sockio.h =================================================================== --- head/sys/sys/sockio.h (revision 62586) +++ head/sys/sys/sockio.h (revision 62587) @@ -1,99 +1,101 @@ /*- * Copyright (c) 1982, 1986, 1990, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)sockio.h 8.1 (Berkeley) 3/28/94 * $FreeBSD$ */ #ifndef _SYS_SOCKIO_H_ #define _SYS_SOCKIO_H_ #include /* Socket ioctl's. */ #define SIOCSHIWAT _IOW('s', 0, int) /* set high watermark */ #define SIOCGHIWAT _IOR('s', 1, int) /* get high watermark */ #define SIOCSLOWAT _IOW('s', 2, int) /* set low watermark */ #define SIOCGLOWAT _IOR('s', 3, int) /* get low watermark */ #define SIOCATMARK _IOR('s', 7, int) /* at oob mark? */ #define SIOCSPGRP _IOW('s', 8, int) /* set process group */ #define SIOCGPGRP _IOR('s', 9, int) /* get process group */ #define SIOCADDRT _IOW('r', 10, struct ortentry) /* add route */ #define SIOCDELRT _IOW('r', 11, struct ortentry) /* delete route */ #define SIOCGETVIFCNT _IOWR('r', 15, struct sioc_vif_req)/* get vif pkt cnt */ #define SIOCGETSGCNT _IOWR('r', 16, struct sioc_sg_req) /* get s,g pkt cnt */ #define SIOCSIFADDR _IOW('i', 12, struct ifreq) /* set ifnet address */ #define OSIOCGIFADDR _IOWR('i', 13, struct ifreq) /* get ifnet address */ #define SIOCGIFADDR _IOWR('i', 33, struct ifreq) /* get ifnet address */ #define SIOCSIFDSTADDR _IOW('i', 14, struct ifreq) /* set p-p address */ #define OSIOCGIFDSTADDR _IOWR('i', 15, struct ifreq) /* get p-p address */ #define SIOCGIFDSTADDR _IOWR('i', 34, struct ifreq) /* get p-p address */ #define SIOCSIFFLAGS _IOW('i', 16, struct ifreq) /* set ifnet flags */ #define SIOCGIFFLAGS _IOWR('i', 17, struct ifreq) /* get ifnet flags */ #define OSIOCGIFBRDADDR _IOWR('i', 18, struct ifreq) /* get broadcast addr */ #define SIOCGIFBRDADDR _IOWR('i', 35, struct ifreq) /* get broadcast addr */ #define SIOCSIFBRDADDR _IOW('i', 19, struct ifreq) /* set broadcast addr */ #define OSIOCGIFCONF _IOWR('i', 20, struct ifconf) /* get ifnet list */ #define SIOCGIFCONF _IOWR('i', 36, struct ifconf) /* get ifnet list */ #define OSIOCGIFNETMASK _IOWR('i', 21, struct ifreq) /* get net addr mask */ #define SIOCGIFNETMASK _IOWR('i', 37, struct ifreq) /* get net addr mask */ #define SIOCSIFNETMASK _IOW('i', 22, struct ifreq) /* set net addr mask */ #define SIOCGIFMETRIC _IOWR('i', 23, struct ifreq) /* get IF metric */ #define SIOCSIFMETRIC _IOW('i', 24, struct ifreq) /* set IF metric */ #define SIOCDIFADDR _IOW('i', 25, struct ifreq) /* delete IF addr */ #define SIOCAIFADDR _IOW('i', 26, struct ifaliasreq)/* add/chg IF alias */ #define SIOCALIFADDR _IOW('i', 27, struct if_laddrreq) /* add IF addr */ #define SIOCGLIFADDR _IOWR('i', 28, struct if_laddrreq) /* get IF addr */ #define SIOCDLIFADDR _IOW('i', 29, struct if_laddrreq) /* delete IF addr */ #define SIOCADDMULTI _IOW('i', 49, struct ifreq) /* add m'cast addr */ #define SIOCDELMULTI _IOW('i', 50, struct ifreq) /* del m'cast addr */ #define SIOCGIFMTU _IOWR('i', 51, struct ifreq) /* get IF mtu */ #define SIOCSIFMTU _IOW('i', 52, struct ifreq) /* set IF mtu */ #define SIOCGIFPHYS _IOWR('i', 53, struct ifreq) /* get IF wire */ #define SIOCSIFPHYS _IOW('i', 54, struct ifreq) /* set IF wire */ #define SIOCSIFMEDIA _IOWR('i', 55, struct ifreq) /* set net media */ #define SIOCGIFMEDIA _IOWR('i', 56, struct ifmediareq) /* get net media */ #define SIOCSIFPHYADDR _IOW('i', 70, struct ifaliasreq) /* set gif addres */ #define SIOCGIFPSRCADDR _IOWR('i', 71, struct ifreq) /* get gif psrc addr */ #define SIOCGIFPDSTADDR _IOWR('i', 72, struct ifreq) /* get gif pdst addr */ +#define SIOCDIFPHYADDR _IOW('i', 73, struct ifreq) /* delete gif addrs */ + #define SIOCSIFGENERIC _IOW('i', 57, struct ifreq) /* generic IF set op */ #define SIOCGIFGENERIC _IOWR('i', 58, struct ifreq) /* generic IF get op */ #define SIOCGIFSTATUS _IOWR('i', 59, struct ifstat) /* get IF status */ #define SIOCSIFLLADDR _IOW('i', 60, struct ifreq) /* set link level addr */ #endif /* !_SYS_SOCKIO_H_ */